下面我有一个PHP脚本,需要搜索XML文件并找到<AnotherChild>
的ID。出于某种原因,目前它返回0个结果,我不知道为什么。如果有人能理解为什么它会返回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;
}
?>
感谢
评论中链接的两个问题确实回答了这个问题,但它们没有足够清楚地说明为什么他们回答了IMO,所以我会在聊天中的回答后面添加这个。
考虑以下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()
,但您错过的关键点是(在PHP的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不包含该前缀,但我们在查询示例中使用了该前缀。
为了理解原因,让我们看看另一段XML:
<baz:root xmlns:baz="http://www.bar.com/">
<baz:child>
<baz:grandchild>foo</baz:grandchild>
</baz:child>
</baz:root>
此文档在语义上与第二个文档相同-代码示例与任何一个(证明)都能很好地工作。前缀与命名空间是分开的。请注意,尽管这在文档中使用了baz:
前缀,XPath仍然使用bar:
前缀。这是因为识别命名空间的思想是URI,而不是前缀。
因此,当文档使用名称空间时,我们必须在XPath中注册名称空间,并使用我们注册的前缀来引用属于该名称空间的任何节点,从而使使用名称空间,而不是针对它。
为了完整起见,当我们将这些原则应用于您的原始文档时,您将与问题中的代码一起使用的查询是:
//transXchange:AnotherChild/transXchange:id
为了解决这个问题,我首先注册了名称空间:
$xpathvar->registerNamespace('transXchange', 'http://www.transxchange.org.uk/');
然后修改查询如下:
$queryResult = $xpathvar->query('//transXchange:AnotherChild/transXchange:id');
这成功返回了ID。