2013-08-08 10 views
6

Poniżej mam skrypt PHP, który muszę przeszukać plik XML i znaleźć identyfikator dla <AnotherChild>. Z jakiegoś powodu, w tej chwili zwraca 0 wyników i nie mogę zrozumieć dlaczego. Jeśli ktokolwiek może zobaczyć, dlaczego zwraca 0 wyników, byłbym bardzo wdzięczny, gdyby mogli mi powiedzieć, dlaczego.PHP XPath search wraca 0 wyniki

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; 
    } 
?> 

Dzięki

+0

możliwe duplikat [XPath z nazw] (http: // stackoverflow.com/questions/9827685/xpath-with-namespace) – Wrikken

+0

[ten może być jednak lepszy] (http://stackoverflow.com/questions/6475394/php-xpath-query-on-xml-with-default- wiążący przestrzeń nazw) – Wrikken

+0

@Wrikken Właśnie zapoznałem się z tymi odpowiedziami i nie widzę, jakbym dostosował mój kod, aby naprawić mój problem? – jskidd3

Odpowiedz

9

Dwa pytania związane z komentarzami rzeczywiście odpowiadają na to pytanie, ale nie dość jasno określają, dlaczego odpowiadają na to IMO, więc dodam to po my answer in chat.


Rozważmy następujący dokument XML:

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

To nie ma xmlns atrybuty w ogóle, co oznacza, że ​​można wyszukać //grandchild i uzyskać wynik można oczekiwać. Każdy węzeł znajduje się w domyślnej przestrzeni nazw , więc można zająć się wszystkim bez rejestracji przestrzeni nazw w XPath.

Teraz pomyśl o tym:

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

Ten deklaruje przestrzeń nazw http://www.bar.com/ iw rezultacie jesteś musi używać tej przestrzeni nazw do rozwiązania węzeł użytkownika.

Jak już ustaliłeś, sposobem na to jest użycie DOMXPath::registerNamespace() - ale kluczowym punktem, który przegapiłeś jest to, że (w PHP XPath implementacji) każda przestrzeń nazw musi być zarejestrowana z prefiksem i musisz użyć ten prefiks do adresów węzłów, które należą do niego. Nie można zarejestrować przestrzeni nazw w XPath z pustym prefiksem.

więc, biorąc pod uwagę Drugi przykład powyżej, pozwala spojrzeć na to, jak chcemy wykonać oryginalną //grandchild zapytanie:

<?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 
    } 

Uwaga jak zarejestrowanego przy użyciu przestrzeni nazw to URI, a my określony prefiks. Mimo że oryginalny plik XML nie zawierał tego prefiksu, używamy prefiksu w zapytaniu - example.

Aby zrozumieć dlaczego, spójrzmy na innym kawałku XML:

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

Niniejszy dokument jest semantycznie identyczne na drugi - przykładowy kod będzie działać równie dobrze z obu (proof). Przedrostek jest oddzielony od przestrzeni nazw. Zwróć uwagę, że nawet jeśli w dokumencie jest używany przedrostek baz:, XPath używa prefiksu bar:. Dzieje się tak, ponieważ myślą, która identyfikuje przestrzeń nazw, jest przedrostek URI, , a nie.

Więc gdy dokument używa nazw, musimy pracować z przestrzeń nazw, a nie przeciwko nim, poprzez rejestrację nazw w XPath i stosując prefiks zarejestrowaliśmy go przed odnieść się do wszelkich węzłów, które należą do tej przestrzeni nazw.

Dla kompletności, gdy zastosowanie tych zasad do oryginalnego dokumentu, zapytanie, które można używać w kodzie w tej kwestii jest:

//transXchange:AnotherChild/transXchange:id 
+0

Dzięki za pozostawienie tak wspaniałej, szczegółowej odpowiedzi! – jskidd3

+0

@JoelKidd Nie ma problemu, w pokoju PHP na czacie jest kilku stałych klientów, którzy dobrze znają XML i XPath, jeśli masz więcej pytań, hakre [blogi] (http://hakre.wordpress.com/) o niektórych z zawiłości okresowo, jeśli chcesz to sprawdzić :-) – DaveRandom

+0

To jest niesamowite! Będę czytał, dodał do zakładek blog. Dzięki jeszcze raz. – jskidd3

2

Aby rozwiązać ten problem raz pierwszy zarejestrowany przestrzeń nazw:

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

A potem zmodyfikowane zapytanie jak więc:

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

Ten kod powrócił pomyślnie.