2009-12-02 4 views
65

Я следующий файл XML, файл является довольно большим, и я не смог получить SimpleXML, чтобы открыть и прочитать файл, так что я пытаюсь XMLReader без успеха в PHPКак использовать 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> 

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

+1

Вы читали некоторые из пользователей способствовали примеры в документации PHP? http://www.php.net/manual/en/class.xmlreader.php#61929 может помочь. – mcrumley

ответ

186

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

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

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

XMLReader + SimpleXML

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

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

XMLReader + DOM

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

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

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

+0

есть ли способ сделать это исключительно с помощью XMLReader или нет никаких преимуществ? –

+2

Вы можете сделать это полностью с помощью XMLReader. Преимущество заключается в том, что оно будет быстрее и будет иметь меньшую память. Недостатком является то, что запись займет гораздо больше времени и будет намного сложнее отладить. –

+1

Почему вы просто не использовали $ z-> next ('product') при переходе на первый узел продукта? – redolent

2

XMLReader хорошо документирован на PHP site. Это 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(); 
?> 

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

+0

как вы его получите, чтобы перейти к следующему узлу после его обработки? –

+20

Кроме того, что XMLReader хорошо документирован на php.net, я бы не согласился, это одна из худших документированных функций, которые я видел, и я давно использую php.net, и это было первое место, которое я решил решить перед запросом здесь :) –

+0

Я не уверен, что вы понимаете, как XMLReader :: read() переходит с одного узла на другой. Класс XMLReader также использует libxml, хорошо известную библиотеку, которая также доступна для PHP, если вы хотите взглянуть на нее. –

8

Для 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(); 
+0

Несмотря на то, что код намного более подробный и ручной способ прохождения через XML, это сэкономит ваше здравомыслие, поскольку DOMDocument и SimpleXML имеют тенденцию держать вас в курсе того, что будет возвращено. – b01

1
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)); 
    } 
} 
1

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

$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(); 
4

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

Я считаю, что самый простой способ использования XMLReader - узнать, какие теги я хочу получить из них и использовать их. Если вы знаете структуру XML и у нее много уникальных тегов, я считаю, что использование первого случая - это просто. Случаи 2 и 3 - это просто показать вам, как это можно сделать для более сложных тегов. Это очень быстро; У меня есть обсуждение скорости на What is the fastest XML parser in 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(); 
} 
0

Я напуган, что с помощью XmlReader :: расширения() может потреблять довольно много оперативной памяти, когда поддерево не так мало. Я не уверен, что это хорошая альтернатива XmlReader. Однако я согласен с тем, что XmlReader действительно слаб и не очень подходит для обработки сложных вложенных XML-деревьев. Мне действительно не нравятся две вещи: во-первых, этот текущий узел не имеет своего пути в дереве 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 с этими классами.

1

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

Моя проблема в том, что я должен прочитать файл 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; 
?> 
+0

Это не отвечает на вопрос - это, по-видимому, просто отдельный пример того, как вы смогли решить другую проблему. –

Смежные вопросы