命名空间和 XPath

Namespaces and XPath

我正在探索 XML 和 PHP,主要是 XPath 和其他解析器。

这里是 xml:

<?xml version="1.0" encoding="UTF-8"?>

<root xmlns:foo="http://www.foo.org/" xmlns:bar="http://www.bar.org">
    <actors>
        <actor id="1">Christian Bale</actor>
        <actor id="2">Liam Neeson</actor>
        <actor id="3">Michael Caine</actor>
    </actors>
    <foo:singers>
        <foo:singer id="4">Tom Waits</foo:singer>
        <foo:singer id="5">B.B. King</foo:singer>
        <foo:singer id="6">Ray Charles</foo:singer>
    </foo:singers>
    <items>
        <item id="7">Pizza</item>
        <item id="8">Cheese</item>
        <item id="9">Cane</item>
    </items>
</root>

这是我的路径和代码:

$xml = simplexml_load_file('xpath.xml');

$result = $xml -> xpath('/root/actors');

echo '<pre>'.print_r($result,1).'</pre>';

现在,说路径 returns:

Array
(
    [0] => SimpleXMLElement Object
        (
            [actor] => Array
                (
                    [0] => Christian Bale
                    [1] => Liam Neeson
                    [2] => Michael Caine
                )
        )
)

虽然看似相似的代码行(我本以为会产生歌手)却没有。含义:

$result = $xml -> xpath('/root/foo:singers');

结果:

Array
    (
        [0] => SimpleXMLElement Object
            (
            )

    )

现在我会认为 foo: 命名空间在这种情况下不是问题,两条路径应该分别产生相同类型的 singers/actors 数组?怎么不是这样?

谢谢!

注意:正如你可能知道的那样,我对 xml 很陌生,所以请保持温和。

编辑:当我去 /root/foo:singers/foo:singer 时,我得到了结果,但之前没有。同样仅使用 /root 我只得到演员和项目作为结果,foo:singers 被完全省略。

您可以使用 // 表达式,例如:

$xml -> xpath( '//foo:singer' );

到 select 所有 foo:singer 元素,无论它们在哪里。

编辑:

SimpleXMLElement 是 selected,你只是看不到带有 print_r() 的子节点。使用像 SimpleXMLElement::children 这样的 SimpleXMLElement 方法来访问它们。

// example 1
$result = $xml->xpath( '/root/foo:singers' );

foreach( $result as $value ) {
    print_r( $value->children( 'foo', TRUE ) );
}

// example 2
print_r( $result[0]->children( 'foo', TRUE )->singer );

出于多种原因,SimpleXML 简直就是一个糟糕的 API。

对于大多数用途,我建议 PHP 的 DOM extension. (Or for very large documents a combination of it along with XMLReader。)

要在 xpath 中使用命名空间,您需要 register those you'd like to use, and the prefix you want to use them with, with your xpath processor


示例:

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

// The prefix *can* match that used in the document, but it's not necessary.
$xpath->registerNamespace("ns", "http://www.foo.org/");

foreach ($xpath->query("/root/ns:singers") as $node) {
    echo $dom->saveXML($node);
}

输出:

<foo:singers>
    <foo:singer id="4">Tom Waits</foo:singer>
    <foo:singer id="5">B.B. King</foo:singer>
    <foo:singer id="6">Ray Charles</foo:singer>
</foo:singers>

DOMXPath::query returns a DOMNodeList containing matched nodes. You can work with it essentially the same way you would in any other language with a DOM 实施。