6

下面我有一个 PHP 脚本,我需要搜索一个 XML 文件并找到<AnotherChild>. 出于某种原因,目前它返回 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;
  }
?>

谢谢

4

2 回答 2

9

评论中链接的两个问题确实回答了这个问题,但他们并没有很清楚地说明他们为什么回答 IMO,所以我将在我的回答之后添加这个 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()- 但您错过的关键点是(在 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:前缀。这是因为标识命名空间的think是URI,而不是前缀。

因此,当文档使用名称空间时,我们必须使用名称空间,而不是反对它,方法是在 XPath 中注册名称空间并使用我们注册它的前缀来引用属于该名称空间的任何节点。

为了完整起见,当我们将这些原则应用于您的原始文档时,您将与问题中的代码一起使用的查询是:

//transXchange:AnotherChild/transXchange:id
于 2013-08-08T22:22:50.163 回答
2

为了解决这个问题,我首先注册了命名空间:

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

然后像这样修改查询:

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

这成功返回了 ID。

于 2013-08-08T21:58:34.167 回答