Como usar XMLReader em PHP?

Eu tenho o seguinte arquivo XML, o arquivo é bastante grande e não consegui obter o simplexml para abrir e ler o arquivo, então estou tentando o XMLReader sem sucesso na php

  2009-11-30 13:52:40  foo foo foo foo   bar bar bar bar   

Desafortunadamente, não encontrei um bom tutorial sobre isso para o PHP e gostaria de ver como posso obter cada conteúdo de elemento para armazenar em um database.

Tudo depende de quão grande a unidade de trabalho, mas acho que você está tentando tratar cada nós em sucessão.

Para isso, a maneira mais simples seria usar o XMLReader para chegar a cada nó e, em seguida, usar o SimpleXML para acessá-los. Desta forma, você mantém o uso da memory baixo porque está tratando um nó por vez e você ainda aproveita a facilidade de uso do SimpleXML. Por exemplo:

 $z = new XMLReader; $z->open('data.xml'); $doc = new DOMDocument; // move to the first  node while ($z->read() && $z->name !== 'product'); // now that we're at the right depth, hop to the next  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  $z->next('product'); } 

Visão geral rápida de prós e contras de diferentes abordagens:

Somente XMLReader

  • Prós: rápido, usa pouca memory

  • Contras: excessivamente difícil de escrever e depurar, exige muito código do userland para fazer qualquer coisa útil. O código do usuário é lento e propenso ao erro. Além disso, deixa você com mais linhas de código para manter

XMLReader + SimpleXML

  • Prós: não usa muita memory (apenas a memory necessária para processar um nó) e o SimpleXML é, como o nome indica, muito fácil de usar.

  • Contras: criar um object SimpleXMLElement para cada nó não é muito rápido. Você realmente tem que compará-lo para entender se é um problema para você. Mesmo assim, uma máquina modesta seria capaz de processar mil nós por segundo.

XMLReader + DOM

  • Prós: usa sobre tanta memory como SimpleXML e XMLReader :: expand () é mais rápido do que criar um novo SimpleXMLElement. Eu queria que fosse possível usar simplexml_import_dom() mas não parece funcionar nesse caso

  • Contras: DOM é irritante para trabalhar. Está a meio caminho entre XMLReader e SimpleXML. Não é tão complicado e estranho como XMLReader, mas anos-luz longe de trabalhar com SimpleXML.

Meu conselho: escreva um protótipo com o SimpleXML, veja se ele funciona para você. Se o desempenho for primordial, tente DOM. Fique tão longe do XMLReader quanto possível. Lembre-se de que quanto mais você escreve, maior a possibilidade de introduzir erros ou introduzir regressões de desempenho.

Para xml formatado com atributos …

data.xml:

      

Código 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(); 

A maior parte da minha vida de análise XML é gasto extraindo nuggets de informações úteis de caminhões de XML (Amazon MWS). Como tal, minha resposta assume que você deseja apenas informações específicas e você sabe onde ela está localizada.

Eu acho que a maneira mais fácil de usar o XMLReader é saber de quais tags eu quero as informações e as uso. Se você conhece a estrutura do XML e tem muitas tags únicas, acho que usar o primeiro caso é fácil. Os casos 2 e 3 são apenas para mostrar como isso pode ser feito para tags mais complexas. Isso é extremamente rápido; Tenho uma discussão de velocidade sobre o que é o analisador XML mais rápido em PHP?

A coisa mais importante a lembrar ao fazer if ($myXML->nodeType == XMLReader::ELEMENT) {... baseadas em tags como esta é usar if ($myXML->nodeType == XMLReader::ELEMENT) {... – que verifica se estamos apenas lidando com abertura de nós e não espaços em branco ou fechando nós ou o que quer que seja.

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

O XMLReader está bem documentado no site PHP . Este é um XML Pull Parser, o que significa que é usado para iterar através de nós (ou DOM Nodes) de determinado documento XML. Por exemplo, você pode passar por todo o documento que você deu assim:

 < ?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(); ?> 

Depende de você decidir como lidar com o nó retornado pelo XMLReader :: expand () .

 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('
') . var_dump($countElements)); } }

A resposta aceita me deu um bom começo, mas trouxe mais classs e mais processamento do que eu teria gostado; então esta é a minha interpretação:

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

Este tópico é encerrado há muito tempo, mas acabei de encontrá-lo. Graças a Deus.

O meu problema é que eu tenho que ler o arquivo ONIX (dados do livro) e armazená-lo em nosso database. Eu uso o simplexml_load antes e, embora tenha usado muita memory, mas ainda está bem para um arquivo relativamente pequeno (até 300 MB). Além desse tamanho, é um desastre para mim.

Depois de ler, especialmente a interpretação de Francis Lewis, uso combinação de xmlreader e simplexml. O resultado é excepcional, o uso de memory é pequeno e insira-o no database o suficiente para mim.

Aqui está o meu código:

 < ?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; ?> 

Tenho medo de que usar XmlReader :: expand () pode consumir bastante RAM quando a subtree não é tão pequena. Não tenho certeza se é uma boa alternativa para o XmlReader. No entanto, eu concordo que o XmlReader é realmente fraco e não é muito adequado para processar trees XML complexas enxertadas. Eu realmente não gosto de duas coisas: primeiro, o nó atual não tem seu caminho na tree XML acessível como propriedade, em segundo lugar, não é possível executar o processamento do XPath ao ler os nós. É claro que a consulta XPath real será muito demorada para o XML grande, mas os “hooks de caminho” podem ser usados ​​em vez disso – como quando o caminho atual do elemento corresponde (subjacente) a uma function PHP / método triggers. Assim, desenvolvi minhas próprias aulas em cima do XmlReader alguns anos atrás. Eles não são perfeitos e talvez eu escreva melhor hoje, no entanto, ainda pode ser útil para alguém:

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

Eu construo o caminho XML ‘node1 / node2’, então use ganchos com as combinações de PCRE que são menos poderosas do que o XPath, no entanto foram suficientes para minhas necessidades. Eu processei um XML grande bastante complexo com essas classs.