Чтение дочерних узлов с помощью XMLReader

Я пытаюсь написать гибридную функцию XMLReader / SimpleXML для чтения очень большого (700 МБ) XML-файла. XML находится в таком формате:

<Items> <Item> <ItemKey>ABCDEF123</ItemKey> <Name> <English>An Item Name</English> <German>An Item Name In German</German> <French>An Item Name In French</French> </Name> <Description> <English>An Item Description</English> <German>An Item Description In German</German> <French>An Item Description In French</French> </Description> </Item> <Item> <ItemKey>GHIJKL456</ItemKey> <Name> <English>Another Item Name</English> <German>Another Item Name In German</German> <French>Another Item Name In French</French> </Name> <Description> <English>Another Item Description</English> <German>Another Item Description In German</German> <French>Another Item Description In French</French> </Description> </Item> </Items> 

Код, который я написал до сих пор, чтобы сделать это:

 $xml = new XMLReader(); if(!$xml->open('testitems.xml')){ die('Failed to open file!'); } else { echo 'File opened'; } $items = array(); while ($xml->read()){ if($xml->nodeType == XMLReader::ELEMENT){ if ($xml->name == 'Item'){ $item = array(); } if ($xml->name == 'ItemKey'){ $xml->read(); $item['itemKey'] = $xml->value; } if ($xml->name == 'Name'){ $sxml = new SimpleXMLElement($xml->readOuterXml()); $englishName = $sxml->English; $item['englishName'] = $englishName; } } if($xml->nodeType == XMLReader::END_ELEMENT){ if ($xml->name == 'Item'){ $items[] = $item; } } } var_dump($items); $xml->close(); 

Однако, в то время как значение узла ItemKey вставляется в массив, имя на английском не является, я не могу нормально обращаться к этому узлу. Я бы просто использовал XMLReader для всего, но так как повторные вхождения английского узла (один для Name, другой для описания) из моего Googling до сих пор SimpleXML казался путём вперед, но радости пока нет.

Какие-либо предложения? Какие-нибудь хорошие гиды? Документация XMLReader на php.net крайне скудна по сравнению со многими другими функциями PHP, и, как правило, сложно найти подробные руководства, которые ясны и кратки.

Related of "Чтение дочерних узлов с помощью XMLReader"

Хорошо, если вы все еще можете построить этот массив, ваш XML-файл, вероятно, не такой большой :). Попробуйте загрузить весь файл с помощью simplexml, например, вы можете быть удивлены тем, что он не потребляет столько памяти.

В любом случае, если вы все еще хотите использовать XMLReader, я часто предлагаю свою библиотеку XMLReader Iterator, которая может перебирать XMLReader для доступа к элементам, XMLReader элементам и делать такие вещи, как превращение фрагментов в SimpleXMLElement s.

Ниже приведен пример, который почти идентичен приведенному выше примеру:

 require('xmlreader-iterators.php'); // https://github.com/hakre/XMLReaderIterator/tree/master/build/include $xmlFile = "xmlreader-17262798.xml"; $reader = new XMLReader(); $reader->open($xmlFile); /* @var $itemIterator XMLReaderNode[] */ $itemIterator = new XMLElementIterator($reader, 'Item'); $items = array(); foreach ($itemIterator as $item) { $xml = $item->asSimpleXML(); $items[] = array( 'itemKey' => (string)$xml->ItemKey, 'englishName' => (string)$xml->Name->English, ); } 

Когда вы запускаете его в своих демо-данных, результирующий массив $items :

 Array ( [0] => Array ( [itemKey] => ABCDEF123 [englishName] => An Item Name ) [1] => Array ( [itemKey] => GHIJKL456 [englishName] => Another Item Name ) ) 

Технически вам не нужно использовать эту библиотеку, она работает только с XMLReader поэтому она не меняет работу XMLReader . Это надстройка.

Почему это не работает в вашем конкретном случае, сложно сказать, ваш код действительно безуспешно работал на моем компьютере:

 Array ( [0] => Array ( [itemKey] => ABCDEF123 [englishName] => SimpleXMLElement Object ( [0] => An Item Name ) ) [1] => Array ( [itemKey] => GHIJKL456 [englishName] => SimpleXMLElement Object ( [0] => Another Item Name ) ) ) 

Поскольку этот вывод print_r из $items (вашего кода) показывает, ключи englishName устанавливаются в элементы simplexml. Возможно, вы захотите отбросить их в строку, как в моем примере (эти две (string) части), чтобы вместо них были строки вместо SimpleXMLElements, что, вероятно, было вашей проблемой. Если нет, проверьте версию libxml:

 var_dump(LIBXML_DOTTED_VERSION); # string(5) "2.7.8" 

И сообщите об этом (то есть на базе библиотеки XMLReader ). Также отлаживайте свой SimpleXMLElement ( var_dump($sxml->asXML()); ), чтобы вы могли проверить, что ожидаемый XML был загружен.

Библиотека, которую я предлагаю кстати. поставляется также с одним включенным файлом, если вы хотите быстро его протестировать.

В прошлый раз я предположил, что библиотека находится в:

  • XML Parsing – невозможно получить значение узла

Редактировать: дополнительная гибридная версия без библиотеки, показывающая использование next() которая полезна, поскольку вы всегда повторяете одноименные братья и сестры: <Item> :

 $xmlFile = "xmlreader-17262798.xml"; $reader = new XMLReader(); $reader->open($xmlFile); $reader->read() && $reader->read(); // init and position onto first element $items = array(); while ($reader->next('Item')) { $node = new SimpleXMLElement($reader->readOuterXML()); $items[] = array( 'itemkey' => $node->ItemKey, 'englishName' => $node->Name->English, 'englishDesc' => $node->Description->English, ); } 

Никогда не думал, понял. Для всех, кто застрял на этом:

 $xml = new XMLReader(); if(!$xml->open('Items.xml')){ die('Failed to open file!'); } else { echo 'File opened'; } $items = array(); while ($xml->read() && $xml->name !== "Item"); while ($xml->name === "Item") { $item = array(); $node = new SimpleXMLElement($xml->readOuterXML()); $item['itemkey'] = $node->ItemKey; $item['englishName'] = $node->Name->English; $item['englishDesc'] = $node->Description->English; $items[] = $item; }