2013-08-08 3 views
6

Ниже у меня есть PHP-скрипт, который мне нужен для поиска по XML-файлу и найти ID для <AnotherChild>. По какой-то причине на данный момент он возвращает 0 результатов, и я не могу понять, почему. Если кто-нибудь может понять, почему он возвращает 0 результатов, я бы очень признателен, если бы они могли сообщить мне, почему.PHP XPath поиск возвращает 0 результатов

XML:

<TransXChange xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.transxchange.org.uk/" xsi:schemaLocation="http://www.transxchange.org.uk/ http://www.transxchange.org.uk/schema/2.1/TransXChange_general.xsd" CreationDateTime="2013-07-12T18:12:21.8122032+01:00" ModificationDateTime="2013-07-12T18:12:21.8122032+01:00" Modification="new" RevisionNumber="3" FileName="swe_44-611A-1-y10.xml" SchemaVersion="2.1"> 
    <Node1>...</Node1> 
    <Node2>...</Node2> 
    <Node3>...</Node3> 
    <Node4>...</Node4> 
    <Node5>...</Node5> 
    <Node6>...</Node6> 
    <Node7> 
     <Child> 
      <id>ABCDEFG123</id> 
     </Child> 
     <AnotherChild> 
      <id>ABCDEFG124</id> 
     </AnotherChild> 
    </Node7> 
    <Node8>...</Node8> 
</TransXChange> 

PHP:

<?php 

    $xmldoc = new DOMDocument(); 
    $xmldoc->load("directory1/directory2/file.xml"); 

    $xpathvar = new DOMXPath($xmldoc); 
    $xpathvar->registerNamespace('transXchange', 'http://www.transxchange.org.uk/'); 

    $queryResult = $xpathvar->query('//AnotherChild/id'); 
    foreach($queryResult as $result) { 
    echo $result->textContent; 
    } 
?> 

Благодаря

+0

возможно дубликат [XPath с пространством имен] (HTTP: // stackoverflow.com/questions/9827685/xpath-with-namespace) – Wrikken

+0

[это может быть лучше, хотя] (http://stackoverflow.com/questions/6475394/php-xpath-query-on-xml-with-default- namespace-binding) – Wrikken

+0

@Wrikken Я только что посмотрел на оба этих ответа и не могу понять, как настроить свой код, чтобы исправить мою проблему? – jskidd3

ответ

9

Ответы на два вопроса, которые заданы в комментариях, действительно отвечают на этот вопрос, но они не совсем понятны почему они отвечают на него IMO, поэтому я добавлю следующее my answer in chat.


Рассмотрим следующий XML-документ:

<root> 
    <child> 
    <grandchild>foo</grandchild> 
    </child> 
</root> 

Это не имеет xmlns атрибуты вообще, что означает, что вы можете запросить //grandchild и получить результат, который вы ожидаете. Каждый узел находится в пространстве имен по умолчанию, поэтому все можно решить без регистрации пространства имен в XPath.

Теперь рассмотрим это:

<root xmlns="http://www.bar.com/"> 
    <child> 
    <grandchild>foo</grandchild> 
    </child> 
</root> 

Объявляет пространство имен http://www.bar.com/ и в результате вы должны использовать это пространство имен для решения узла члена.

Как вы уже поняли, способ сделать это состоит в использовании DOMXPath::registerNamespace() - но важный момент, что вы пропустили, что (в РНР реализация XPath) каждое пространство имен должно быть зарегистрировано с префиксом, и вы должны использовать этот префикс для адреса узлов, принадлежащих ему. Невозможно зарегистрировать пространство имен в XPath с пустым префиксом.

Таким образом, учитывая второй пример выше, давайте посмотрим на то, как мы должны выполнить оригинальный //grandchild запрос:

<?php 

    $doc = new DOMDocument(); 
    $doc->loadXML($xml); 

    $xpath = new DOMXPath($doc); 
    $xpath->registerNamespace('bar', 'http://www.bar.com/'); 

    $nodes = $xpath->query('//bar:grandchild'); 
    foreach($nodes as $node) { 
     // do stuff with $node 
    } 

Обратите внимание, как мы зарегистрировали пространство имен, используя его URI, и мы указали префикс. Хотя исходный XML не содержит этого префикса, мы используем префикс в запросе - example.

Чтобы понять, почему, давайте посмотрим на другую часть XML:

<baz:root xmlns:baz="http://www.bar.com/"> 
    <baz:child> 
    <baz:grandchild>foo</baz:grandchild> 
    </baz:child> 
</baz:root> 

Этот документ семантически тождественны к второму - образец кода будет одинаково хорошо работать как с (proof). Префикс отделен от пространства имен. Обратите внимание, что, хотя в документе используется префикс baz:, XPath использует префикс bar:. Это связано с тем, что для определения пространства имен используется URI, , а не префикс.

Так, если документ использует пространство имен, мы должны работать с пространства имен, а не против него, путем регистрации имен в XPath и используя префикс мы зарегистрировали его против того, чтобы ссылаться на любые узлы, которые принадлежат этому пространству имен.

Для полноты, когда мы применяем эти принципы к исходному документу, запрос, который вы будете использовать с кодом в вопросе:

//transXchange:AnotherChild/transXchange:id 
+0

Спасибо, что оставили такой замечательный подробный ответ! – jskidd3

+0

@JoelKidd Нет проблем, есть несколько завсегдатаев в комнате PHP в чате, которые хорошо знакомы с XML и XPath, если у вас есть больше запросов, hakre [blogs] (http://hakre.wordpress.com/) о некоторых из тонкостей, если вы хотите проверить это :-) – DaveRandom

+0

Это потрясающе! У меня будет прочитанный блог в блоге. Еще раз спасибо. – jskidd3

2

Чтобы устранить эту проблему, я первый зарегистрировавший имен:

$xpathvar->registerNamespace('transXchange', 'http://www.transxchange.org.uk/'); 

И затем модифицированную запрос как так:

$queryResult = $xpathvar->query('//transXchange:AnotherChild/transXchange:id'); 

Это верно вернуло идентификатор.

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