Как использовать XMLReader в PHP? - PullRequest
73 голосов
/ 02 декабря 2009

У меня есть следующий 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 и хотел бы посмотреть, как я могу получить содержимое каждого элемента для хранения в базе данных.

Ответы [ 6 ]

208 голосов
/ 02 декабря 2009

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

Для этого самым простым способом было бы использовать XMLReader для доступа к каждому узлу, а затем использовать SimpleXML для доступа к ним. Таким образом, вы сохраняете низкое использование памяти, потому что обрабатываете один узел за раз и при этом используете простоту использования 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

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

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

XMLReader + SimpleXML

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

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

XMLReader + DOM

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

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

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

9 голосов
/ 05 октября 2012

Для 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 код:

$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();
6 голосов
/ 15 октября 2014

Большая часть моей жизни, связанной с анализом 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's 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 голосов
/ 20 июня 2014

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

$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();
2 голосов
/ 10 октября 2013
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));
    }
}
2 голосов
/ 02 декабря 2009

XMLReader хорошо документирован на сайте PHP . Это синтаксический анализатор XML Pull, который означает, что он используется для итерации по узлам (или узлам 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();
?>

Тогда вам решать, как обращаться с узлом, возвращаемым XMLReader :: expand () .

...