2015-04-09 5 views
2

Мне пришлось переписать часть программы для использования XMLReader для выбора частей XML-файла для обработки.Выбор родительских узлов с помощью XMLReader

Возьмите этот упрощенный XML в качестве примера:

<odds> 
    <sport> 
     <region> 
      <group> 
       <event name="English Championship 2014-15" eventid="781016.1"> 
        <bet name="Kazanan" betid="12377108.1"> 
         <selection selectionid="52411062.1"/> 
         </selection> 
        </bet> 
       </event> 
      </group> 
     </region> 
    </sport> 
</odds> 

Этот вызов xpath():

$bets = $xml->xpath(
    "//odds/sport/region/group/event/bet/selection[contains(@selectionid,'".$selectionToFind."')]/.." 
    ); 

выберет весь <bet> узел и его потомков (<selection> узлы).

Мой код, однако, будет выбран только один <selection> узел с заданным selectionid:

$reader = new XMLReader; 
$reader->open('file.xml'); 

while($reader->read()) { 
    $event = $reader->getAttribute($value); 

    if ($event == 781016.1) { 
     $node = new SimpleXMLElement($reader->readOuterXML()); 
     var_dump($node); 
     break; 
    } 
} 

Как реплицировать поведение xpath() с XMLReader так что я выбираю <bet> узел и его потомков, а не только один <selection> ребенок?

Я думаю, вопрос сводится к следующему: Можно ли выбрать весь родительский узел <bet> значением атрибута дочернего элемента, например. <selection selectionid="[some_value]">?

+0

2 вещи. У вас на самом деле есть только один узел выбора в вашем XML. И во-вторых, ваш код должен выбрать заметку «событие», как сейчас, а не «ставку». Заметка «event» имеет атрибут «eventid». –

+0

@ Ясен Желев, извините, я уже включил редактирование. Фактически в моем коде есть переменная '$ value'. Он также инкапсулирован в методе класса. Я просто воспроизвел упрощенные представления - то же, что и XML - он намного больше. Я думаю, вопрос сводится к: Можно ли выбрать весь родительский узел '' значением атрибута дочернего элемента, например. ''? – luqo33

+0

ОК, проверьте мой обновленный ответ. Я думаю, это поможет вам решить эту проблему. –

ответ

1

[Игнорировать решение SimpleXML и смотреть вниз на XMLReader один]

Я предложил бы использовать метод SimpleXMLElement :: XPATH.

http://php.net/manual/en/simplexmlelement.xpath.php

$xml = new SimpleXMLElement($xml_string); 

/* Search for <a><b><c> */ 
$result = $xml->xpath("/odds/sport/region/group/event/bet"); 

$ результат будет содержать все дети записки 'бет'.

// XMLReader раствор **********************

$reader = new XMLReader; 
$reader->open('file.xml'); 
$parent_element = null; 

while($reader->read()) { 
    $selectionid = $reader->getAttribute('selectionid'); 

    if ($selectionid == '52411062.1') { 
     // use the parent of the node with attribute 'selectionid' = '52411062.1' 
     $node = $parent_element; 
     var_dump($node); 
     break; 
    } 
    elseif ($reader->name === 'bet') {) 
    { 
     // store parent element 
     $parent_element = new SimpleXMLElement($reader->readOuterXML()); 
    } 
} 
+0

Я мог бы выполнить запрос 'xpath()' на SimpleXMLElement' всего XML-файла. Это, однако, нецелесообразно, так как XML-файл составляет 14 МБ. Я прибегал к обходу XML с помощью XMLReader, чтобы выбрать только те узлы, которые мне нужны, и только затем преобразовать их в SimpleXMLElement. Это связано с проблемами производительности, вызванными загрузкой всего XML-файла как «SimpleXMLElement». – luqo33

+0

ага, я вижу. Позвольте мне подумать об этом ... –

+0

На самом деле, 'SimpleXML' никогда не должен использоваться, когда есть другие библиотеки XML, потому что он очень быстро ломается. XMLReader не поддерживает XPath - почему бы просто не использовать DOMXPath? См. http://www.ibm.com/developerworks/library/x-xpathphp/. –

0

DOMXPath считается более надежной, чем SimpleXML по отношению к производительности (он имеет другие преимущества, например, он может правильно обрабатывать пространства имен). См. Например, this IBM article для обсуждения нескольких библиотек XPath в PHP.

Мне просто интересно, если ваша проблема производительности будет сохраняться (или по-прежнему быть тяжелой) при использовании DOMXPath:

<?php 

$doc = new DOMDocument; 
$doc->load('sample.xml'); 
$xpath = new DOMXPath($doc); 

$nodes = $xpath->query("/odds/sport/region/group/event/bet[selection/@selectionid = '52411062.1']"); 

foreach ($nodes as $node) 
{ 
    print $xml = $node->ownerDocument->saveXML($node); 
} 
?> 

Результат, принимая в качестве входных данных небольшой фрагмент кода вы показали, это

<bet name="Kazanan" betid="12377108.1"> 
    <selection selectionid="52411062.1"/> 
</bet> 

Если это не помогает, вам действительно нужно прибегнуть к XML-парсеру, основанному на событиях (pull-style), который не читает весь документ в памяти - как предлагает Ясен.

+0

Есть только один вопрос. Если 'DOMXPath' загружает весь xml-файл в память и ведет себя аналогично' simplexml_load_file', выполняя операции над 'SimpleXMLElement', то я не смогу использовать его из-за удара производительности. Я еще не тестировал это, но уверен. – luqo33

+0

@ luqo33 Это так, но, как я уже сказал, мне любопытно, есть ли какие-либо различия между 'SimpleXML' и' DOMXPath' в этом отношении, поэтому, пожалуйста, дайте мне знать, как только у вас будет возможность его протестировать. –

+0

замечают, что я использовал 'XMLReader' для перемещения файла, а затем конвертировал только те узлы, которые мне нужны, в' SimpleXMLElement', чтобы выполнять дальнейшие операции над ними. Я хотел избежать загрузки всего XML как объекта SimpleXMLElement. До сих пор мне удалось увидеть, что этот метод ускоряет работу. Проблема заключается в том, что возможности выбора узлов XMLReader не настолько надежны, как у xpath() 's – luqo33

0

XMLReader может expand() текущий узел в DOMNode. Это будет загружать только узел и его потомков в память.

После этого вы можете использовать экземпляр DOMXPath или преобразовать узел в SimpleXMLElement.

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

$dom = new DOMDocument(); 
$xpath = new DOMXpath($dom); 

while($reader->read()) { 
    if (
    $reader->nodeType == XMLReader::ELEMENT && 
    $reader->localName == 'bet' 
) { 
    $bet= $reader->expand($dom); 
    if ($xpath->evaluate('count(selection[@selectionid = "52411062.1"]) > 0', $bet)) { 
     var_dump($dom->saveXml($bet)); 
    } 
    } 
} 

Вы всегда должны решить, какую часть реализовать в XMLReader и которые в DOM/SimpleXML. В XMLReader вам придется проверять узлы и поддерживать состояние, но можно избежать загрузки данных. В какой-то момент разбора фрагменты XML будут достаточно малы, и вы можете использовать expand().

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