xml — получить определенный тег с помощью XMLReader в стеке переполнения

У меня есть следующая XML-структура в моем XML-файле (это не весь XML-файл, а только его часть):

<?xml version="1.0" encoding="utf-8"?>
<extensions>
<extension extensionkey="fp_product_features">
<downloadcounter>355</downloadcounter>
<version version="0.1.0">
<title>Product features</title>
<description/>
<downloadcounter>24</downloadcounter>
<state>beta</state>
<reviewstate>0</reviewstate>
<category>plugin</category>
<lastuploaddate>1142878270</lastuploaddate>
<uploadcomment> added related features</uploadcomment>
</version>
</extension>
</extensions>

Файл слишком большой для SimpleXML, поэтому я использую XMLReader. У меня есть переключатель, который проверяет XML-теги и их содержимое:

while ($xmlReader->read()) {

if ($xmlReader->nodeType == XMLReader::ELEMENT) {

switch ($xmlReader->name) {

case "title" :

$xmlReader->read();
$foo = $xmlReader->value;
//Do stuff with the value

break;

case  "description":

$xmlReader->read();
$bar = $xmlReader->value;
//Do stuff with the value

break;

case "downloadcounter" :

$xmlReader->read();
$foobar = $xmlReader->value;
//Do stuff with the value

break;

case "state" :

$xmlReader->read();
$barfoo = $xmlReader->value;
//Do stuff with the value

break;//Repeat for other tags

}
}
}

Проблема здесь в том, что есть два <downloadcounter> теги. Тот под <extension> и тот, что под <version>, Мне нужен тот внизу <version>, но код в моем переключателе дает мне один ниже <extension>, Все остальные случаи дают мне правильную информацию.

Я думал о некоторых решениях. Может быть, есть способ указать, что XMLReader читает тег только после <description>? Я использую $xmlReader->read() функционировать несколько раз в одном случае, но это не помогло.
Я очень новичок в этом, так что, возможно, это неправильный способ сделать это, но если кто-то может указать мне правильное направление, это будет высоко ценится.

Заранее спасибо!

0

Решение

Хорошо, некоторые заметки по этому …

Файл слишком большой для SimpleXML, поэтому я использую XMLReader.

Это означало бы, что загрузка XML-файла с помощью SimpleXML достигает PHP memory_limit, верно?
Альтернативой может быть потоковое чтение или чтение фрагмента файла XML и обработка частей.

$xml_chunk = (.... read file chunked ...)
$xml = simplexml_load_string($xml_chunk);
$json = json_encode($xml);
$array = json_decode($json,TRUE);

Но работа с XMLReader — это хорошо!

Может быть, есть способ, где я могу указать, что XMLReader читает только
тег после?

Да, есть. Как указывалось в «i alarmed alien»: если вы работаете с DomDocument, вы можете использовать запрос Xpath, чтобы достичь нужного вам (узел | элемент | элемент).

$dom = new DomDocument();
$dom->load("tooBig.xml");
$xp = new DomXPath($dom);

$result = $xp->query("/extensions/extension/version/downloadcounter");

print $result->item(0)->nodeValue ."\n";

Дополнительные примеры см. В руководстве по PHP: http://php.net/manual/de/domxpath.query.php


Если вы хотите придерживаться XMLReader:

Расширение XMLReader представляет собой синтаксический анализатор XML Pull. Читатель идет вперед по потоку документов, останавливаясь на каждом узле в пути. Это объясняет, почему вы получаете первое из-под тега, а не тот, что ниже.
Это усложняет итерации, потому что заглядывание вперед и прочее невозможно без перечитывания.

DEMO http://ideone.com/Oykfyh

<?php

$xml = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<extensions>
<extension extensionkey="fp_product_features">
<downloadcounter>355</downloadcounter>
<version version="0.1.0">
<title>Product features</title>
<description/>
<downloadcounter>24</downloadcounter>
<state>beta</state>
<reviewstate>0</reviewstate>
<category>plugin</category>
<lastuploaddate>1142878270</lastuploaddate>
<uploadcomment> added related features</uploadcomment>
</version>
</extension>
</extensions>
XML;

$reader = new XMLReader();
$reader->open('data:/text/plain,'.urlencode($xml));

$result = [];
$element = null;

while ($reader->read()) {

if($reader->nodeType === XMLReader::ELEMENT)
{
$element = $reader->name;

if($element === 'extensions') {
$result['extensions'] = array();
}

if($element === 'extension') {
$result['extensions']['extension'] = array();
}

if($element === 'downloadcounter') {
if(!is_array($result['extensions']['extension']['version'])) {
$result['extensions']['extension']['downloadcounter'] = '';
} /*else {
$result['extensions']['extension']['version']['downloadcounter'] = '';
}*/
}

if($element === 'version') {
$result['extensions']['extension']['version'] = array();
while ($reader->read()) {
if($reader->nodeType === XMLReader::ELEMENT)
{
$element = $reader->name;
$result['extensions']['extension']['version'][$element] = '';
}
if($reader->nodeType === XMLReader::TEXT)
{
$value = $reader->value;
$result['extensions']['extension']['version'][$element] = $value;
}
}
}
}

if($reader->nodeType === XMLReader::TEXT)
{
$value = $reader->value;

if($element === 'downloadcounter') {
if(!is_array($result['extensions']['extension']['version'])) {
$result['extensions']['extension']['downloadcounter'] = $value;
}
if(is_array($result['extensions']['extension']['version'])) {
$result['extensions']['extension']['version']['downloadcounter'] = $value;
}
}
}
}
$reader->close();

echo var_export($result, true);

Результат:

array (
'extensions' =>
array (
'extension' =>
array (
'downloadcounter' => '355',
'version' =>
array (
'title' => 'Product features',
'description' => '',
'downloadcounter' => '24',
'state' => 'beta',
'reviewstate' => '0',
'category' => 'plugin',
'lastuploaddate' => '1142878270',
'uploadcomment' => ' added related features',
),
),
),
)

Это преобразует ваш XML в массив (с вложенными массивами).
Это не совсем идеально, из-за ненужных итераций.
Не стесняйтесь взломать …

Дополнительно:
Разбор огромных XML-файлов в PHP
https://github.com/prewk/XmlStreamer

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]