У меня есть следующая 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()
функционировать несколько раз в одном случае, но это не помогло.
Я очень новичок в этом, так что, возможно, это неправильный способ сделать это, но если кто-то может указать мне правильное направление, это будет высоко ценится.
Заранее спасибо!
Хорошо, некоторые заметки по этому …
Файл слишком большой для 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. Читатель идет вперед по потоку документов, останавливаясь на каждом узле в пути. Это объясняет, почему вы получаете первое из-под тега, а не тот, что ниже.
Это усложняет итерации, потому что заглядывание вперед и прочее невозможно без перечитывания.
<?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
Других решений пока нет …