Как использовать XMLReader в PHP?

У меня есть следующий XML файл, файл довольно большой, и я не смог заставить simplexml открывать и читать файл, поэтому я пытаюсь использовать XMLReader без успеха в php

<?xml version="1.0" encoding="ISO-8859-1"?>
<products>
    <last_updated>2009-11-30 13:52:40</last_updated>
    <product>
        <element_1>foo</element_1>
        <element_2>foo</element_2>
        <element_3>foo</element_3>
        <element_4>foo</element_4>
    </product>
    <product>
        <element_1>bar</element_1>
        <element_2>bar</element_2>
        <element_3>bar</element_3>
        <element_4>bar</element_4>
    </product>
</products>

Я, к сожалению, не нашел хорошего учебника по этому поводу для PHP и хотел бы узнать, как я могу получить содержимое каждого элемента для хранения в базе данных.

Ответ 1

Все зависит от того, насколько велика единица работы, но я думаю, вы пытаетесь обрабатывать каждый узел <product/> подряд.

Для этого самым простым способом было бы использовать XMLReader для доступа к каждому node, а затем использовать SimpleXML для доступа к ним. Таким образом, вы сохраняете использование памяти на низком уровне, потому что одновременно рассматриваете один node, и вы по-прежнему используете SimpleXML. Например:

$z = new XMLReader;
$z->open('data.xml');

$doc = new DOMDocument;

// move to the first <product /> node
while ($z->read() && $z->name !== 'product');

// now that we're at the right depth, hop to the next <product/> until the end of the tree
while ($z->name === 'product')
{
    // either one should work
    //$node = new SimpleXMLElement($z->readOuterXML());
    $node = simplexml_import_dom($doc->importNode($z->expand(), true));

    // now you can use $node without going insane about parsing
    var_dump($node->element_1);

    // go to next <product />
    $z->next('product');
}

Быстрый обзор плюсов и минусов различных подходов:

Только XMLReader

  • Плюсы: быстрый, мало памяти

  • Минусы: чрезмерно трудно писать и отлаживать, требуется много кода пользователя, чтобы делать что-нибудь полезное. Код Userland медленный и подвержен ошибкам. Кроме того, он оставляет вам больше строк кода для поддержки

XMLReader + SimpleXML

  • Плюсы: не использует много памяти (только память, необходимая для обработки одного node), а SimpleXML, как следует из названия, очень проста в использовании.

  • Минусы: создание объекта SimpleXMLElement для каждого node выполняется не очень быстро. Вам действительно нужно сравнить его, чтобы понять, является ли это проблемой для вас. Однако даже скромная машина сможет обрабатывать тысячи узлов в секунду.

XMLReader + DOM

  • Плюсы: использует примерно столько же памяти, как SimpleXML, а XMLReader:: expand() быстрее, чем создание нового элемента SimpleXMLElement. Я хотел бы использовать simplexml_import_dom(), но в этом случае он не работает

  • Минусы: DOM раздражает работать. Это на полпути между XMLReader и SimpleXML. Не так сложно и неудобно, как XMLReader, но на несколько световых лет от работы с SimpleXML.

Мой совет: напишите прототип с SimpleXML, посмотрите, работает ли он для вас. Если производительность имеет первостепенное значение, попробуйте DOM. Оставайтесь как можно дальше от XMLReader. Помните, что чем больше кода вы пишете, тем выше вероятность появления ошибок или введения регрессий производительности.

Ответ 2

Для xml, отформатированных с атрибутами...

data.xml:

<building_data>
<building address="some address" lat="28.902914" lng="-71.007235" />
<building address="some address" lat="48.892342" lng="-75.0423423" />
<building address="some address" lat="58.929753" lng="-79.1236987" />
</building_data>

php code:

$reader = new XMLReader();

if (!$reader->open("data.xml")) {
    die("Failed to open 'data.xml'");
}

while($reader->read()) {
  if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'building') {
    $address = $reader->getAttribute('address');
    $latitude = $reader->getAttribute('lat');
    $longitude = $reader->getAttribute('lng');
}

$reader->close();

Ответ 3

Большая часть моей работы по анализу XML тратится на извлечение самородков полезной информации из грузовиков XML (Amazon MWS). Таким образом, мой ответ предполагает, что вы хотите получить только конкретную информацию, и знаете, где она находится.

Я считаю, что самый простой способ использования XMLReader - узнать, какие теги я хочу получить из них и использовать. Если вы знаете структуру XML и у нее много уникальных тегов, я считаю, что использование первого случая - это просто. Случаи 2 и 3 - это просто показать вам, как это можно сделать для более сложных тегов. Это очень быстро; У меня есть обсуждение скорости на Что такое самый быстрый XML-парсер в PHP?

Самое важное, что нужно учитывать при анализе на основе тегов, это использовать if ($myXML->nodeType == XMLReader::ELEMENT) {... - который проверяет, что мы имеем дело только с открытыми узлами, а не с пробелами или закрывающими узлами или что-то еще.

function parseMyXML ($xml) { //pass in an XML string
    $myXML = new XMLReader();
    $myXML->xml($xml);

    while ($myXML->read()) { //start reading.
        if ($myXML->nodeType == XMLReader::ELEMENT) { //only opening tags.
            $tag = $myXML->name; //make $tag contain the name of the tag
            switch ($tag) {
                case 'Tag1': //this tag contains no child elements, only the content we need. And it unique.
                    $variable = $myXML->readInnerXML(); //now variable contains the contents of tag1
                    break;

                case 'Tag2': //this tag contains child elements, of which we only want one.
                    while($myXML->read()) { //so we tell it to keep reading
                        if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Amount') { // and when it finds the amount tag...
                            $variable2 = $myXML->readInnerXML(); //...put it in $variable2. 
                            break;
                        }
                    }
                    break;

                case 'Tag3': //tag3 also has children, which are not unique, but we need two of the children this time.
                    while($myXML->read()) {
                        if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Amount') {
                            $variable3 = $myXML->readInnerXML();
                            break;
                        } else if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Currency') {
                            $variable4 = $myXML->readInnerXML();
                            break;
                        }
                    }
                    break;

            }
        }
    }
$myXML->close();
}

Ответ 4

XMLReader хорошо документирован на PHP-сайте. Это XML Pull Parser, что означает, что он использовался для итерации через узлы (или узлы DOM) данного XML-документа. Например, вы можете пройти весь документ, который вы указали так:

<?php
$reader = new XMLReader();
if (!$reader->open("data.xml"))
{
    die("Failed to open 'data.xml'");
}
while($reader->read())
{
    $node = $reader->expand();
    // process $node...
}
$reader->close();
?>

Затем вам решать, как обращаться с node, возвращенным XMLReader:: expand().

Ответ 5

Simple example:

public function productsAction()
{
    $saveFileName = 'ceneo.xml';
    $filename = $this->path . $saveFileName;
    if(file_exists($filename)) {

    $reader = new XMLReader();
    $reader->open($filename);

    $countElements = 0;

    while($reader->read()) {
        if($reader->nodeType == XMLReader::ELEMENT) {
            $nodeName = $reader->name;
        }

        if($reader->nodeType == XMLReader::TEXT && !empty($nodeName)) {
            switch ($nodeName) {
                case 'id':
                    var_dump($reader->value);
                    break;
            }
        }

        if($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == 'offer') {
            $countElements++;
        }
    }
    $reader->close();
    exit(print('<pre>') . var_dump($countElements));
    }
}

Ответ 6

Принятый ответ дал мне хорошее начало, но принес больше классов и больше обработки, чем мне бы хотелось; так что это моя интерпретация:

$xml_reader = new XMLReader;
$xml_reader->open($feed_url);

// move the pointer to the first product
while ($xml_reader->read() && $xml_reader->name != 'product');

// loop through the products
while ($xml_reader->name == 'product')
{
    // load the current xml element into simplexml and we’re off and running!
    $xml = simplexml_load_string($xml_reader->readOuterXML());

    // now you can use your simpleXML object ($xml).
    echo $xml->element_1;

    // move the pointer to the next product
    $xml_reader->next('product');
}

// don’t forget to close the file
$xml_reader->close();

Ответ 7

Этот вопрос давно ушел, но я только что нашел его. Слава Богу.

Моя проблема в том, что я должен прочитать файл ONIX (данные книги) и сохранить его в нашей базе данных. Я использую simplexml_load раньше, и хотя он использовал много памяти, но все еще хорошо для относительно небольшого файла (до 300 МБ). Помимо этого размера для меня катастрофа.

После прочтения, особенно интерпретации Фрэнсиса Льюиса, я использую комбинацию xmlreader и simplexml. Результат исключителен, использование памяти невелико и вставляет его в базу данных достаточно быстро для меня.

Вот мой код:

<?php
$dbhost = "localhost"; // mysql host
$dbuser = ""; //mysql username
$dbpw = ""; // mysql user password
$db = ""; // mysql database name

//i need to truncate the old data first
$conn2 = mysql_connect($dbhost, $dbuser, $dbpw);
mysql_select_db($db);
mysql_query ("truncate ebiblio",$conn2);
//$xmlFile = $_POST['xmlFile'];
//$xml=simplexml_load_file("ebiblio.xml") or die("Error: Cannot create    object");

$reader = new XMLReader();

//load the selected XML file to the DOM
if (!$reader->open("ebiblio.xml")) {
die("Failed to open 'ebiblio.xml'");
}

while ($reader->read()):

if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'product'){
        $xml = simplexml_load_string($reader->readOuterXML());
        $productcode = (string)$xml->a001;
        $title = (string)$xml->title->b203;
        $author = (string)$xml->contributor->b037;
        $language = (string)$xml->language->b252;
        $category = $xml->subject->b069;
        $description = (string)$xml->othertext->d104;
        $publisher = (string)$xml->publisher->b081;
        $pricecover = (string)$xml->supplydetail->price->j151;
        $salesright = (string)$xml->salesrights->b090;

        @$productcode1 = htmlentities($productcode,ENT_QUOTES,'latin1_swedish_ci');
        @$title1 = htmlentities($title,ENT_QUOTES,'latin1_swedish_ci');
        @$author1 = htmlentities($author,ENT_QUOTES,'latin1_swedish_ci');
        @$language1 = htmlentities($language,ENT_QUOTES,'latin1_swedish_ci');
        @$category1 = htmlentities($category,ENT_QUOTES,'latin1_swedish_ci');
        @$description1 = htmlentities($description,ENT_QUOTES,'latin1_swedish_ci');
        @$publisher1 = htmlentities($publisher,ENT_QUOTES,'latin1_swedish_ci');
        @$pricecover1 = htmlentities($pricecover,ENT_QUOTES,'latin1_swedish_ci');
        @$salesright1 = htmlentities($salesright,ENT_QUOTES,'latin1_swedish_ci');

        $conn = mysql_connect($dbhost, $dbuser, $dbpw);
        mysql_select_db($db);

        $sql = "INSERT INTO ebiblio VALUES ('" . $productcode1 . "','" . $title1 . "','" . $author1 . "','" . $language1 . "','" . $category1 . "','" . $description1 . "','" . $publisher1 . "','" . $pricecover1 . "','" . $salesright1 . "')";

        mysql_query($sql, $conn);
        $reader->next('product');

}


endwhile;
?>

Ответ 8

Я боюсь, что использование XmlReader:: expand() может потреблять довольно много ОЗУ, когда поддерево не так мало. Я не уверен, что это хорошая альтернатива XmlReader. Однако я согласен с тем, что XmlReader действительно слаб и не очень подходит для обработки сложных вложенных XML-деревьев. Мне действительно не нравятся две вещи: во-первых, что текущий node не имеет этого пути в дереве XML, доступном как свойство, во-вторых, что при чтении узлов не удается запустить XPath-подобную обработку. Конечно, реальный запрос XPath будет очень трудоемким для больших XML, но вместо этого можно использовать "крючки пути" - например, когда текущий путь к элементу соответствует корневому поддереву, запускается функция/метод PHP. Таким образом, несколько лет назад я разработал свои собственные классы поверх XmlReader. Они не идеальны и, возможно, я бы лучше написал сегодня, но все равно может быть кому-то полезен:

https://bitbucket.org/sdvpartnership/questpc-framework/src/c481a8b051dbba0a6644ab8a77a71e58119e7441/includes/Xml/Reader/?at=master

Я создаю XML-путь 'node1/node2', а затем использую крючки с совпадениями PCRE, которые менее эффективны, чем XPath, однако для моих нужд было достаточно. Я обработал довольно сложные большие XML с этими классами.