如何在PHP中通过属性选择XML元素而无需迭代?

3
我有这个来自http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml的XML。
<?xml version="1.0" encoding="UTF-8"?>
<gesmes:Envelope xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01" xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref">
    <gesmes:subject>Reference rates</gesmes:subject>
    <gesmes:Sender>
        <gesmes:name>European Central Bank</gesmes:name>
    </gesmes:Sender>
    <Cube>
        <Cube time='2013-08-23'>
            <Cube currency='USD' rate='1.3355'/>
            <Cube currency='GBP' rate='0.85910'/>
            <Cube currency='HUF' rate='298.98'/>
        </Cube>
    </Cube>
</gesmes:Envelope>

为了演示目的,我删除了一些值。

我想使用PHP获取转换率,例如GBP。

可以像这样使用simplexml加载:

$XML=simplexml_load_file("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");

    foreach($XML->Cube->Cube->Cube as $rate){
...

但我希望能够在不迭代的情况下获取该值,而且我真的不想使用正则表达式...

我尝试了类似于这样的方法,但它没有起作用:

$sxe=simplexml_load_file("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml");

$sxe->registerXPathNamespace('c', 'http://www.gesmes.org/xml/2002-08-01');
$result = $sxe->xpath('//c:Cube[@currency="USD"]');

出了什么问题?具体是哪方面的“没起作用”? - michi
我刚刚收到了一个空数组。 - The Surrican
可能是因为你的 XML 中没有'RUB'? - michi
抱歉,我删掉了一些行以使它更短,并删除了RUB的那一行,我将其更改为USD... - The Surrican
1个回答

4

Cube元素不在命名空间http://www.gesmes.org/xml/2002-08-01中,该命名空间已被赋予前缀gesmes,它们位于默认命名空间http://www.ecb.int/vocabulary/2002-08-01/eurofxref中。

因此,应该这样写:

$sxe->registerXPathNamespace('c', 'http://www.gesmes.org/xml/2002-08-01');
$result = $sxe->xpath('//c:Cube[@currency="USD"]');

您需要注册另一个命名空间:

$sxe->registerXPathNamespace('c', 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref');
$result = $sxe->xpath('//c:Cube[@currency="USD"]');

这里是一个演示结果计数为1的现场演示,并且已经纠正了命名空间。


为了尝试给出权威解释,请考虑以下摘自XML中的命名空间规范的摘录:

前缀提供限定名称的命名空间前缀部分,并且必须在命名空间声明中与命名空间URI引用相关联[...]请注意,前缀仅充当命名空间名称的占位符。

与有前缀的元素或属性名称相对应的扩展名称具有绑定到其前缀的URI作为其命名空间名称[...]

默认命名空间声明适用于其范围内的所有无前缀元素名称。

如果存在作用域中的默认命名空间声明,则与无前缀元素名称对应的扩展名称具有默认命名空间的URI作为其命名空间名称。如果没有作用域中的默认命名空间声明,则命名空间名称没有值。[...]在所有情况下,本地名称都是[...]与未加前缀的名称本身相同。

元素Cube没有前缀,因此我们寻找适用范围内的默认命名空间声明;到达最外层元素,我们发现xmlns="http://www.ecb.int/vocabulary/2002-08-01/eurofxref",因此Cube元素的"命名空间名称"是URIhttp://www.ecb.int/vocabulary/2002-08-01/eurofxref,而"本地名称"是Cube

如果我们转而查看元素gesmes:Sender,则会看到它有一个前缀gesmes,因此我们将寻找该前缀的定义。找到xmlns:gesmes="http://www.gesmes.org/xml/2002-08-01",我们得出"扩展名称"具有"命名空间名称"http://www.gesmes.org/xml/2002-08-01,而"本地名称"是Sender

为了在XPath中使用这些"命名空间名称",我们必须分配用于XPath表达式中的前缀,它们不需要与实际文档中的前缀相对应。在这种情况下,我们选择将命名空间http://www.ecb.int/vocabulary/2002-08-01/eurofxref分配给前缀c,以便表达式//c:Cube将匹配具有"命名空间名称"http://www.ecb.int/vocabulary/2002-08-01/eurofxref和"本地名称"Cube的任何元素。


它运行得很好,太棒了!我不完全明白为什么gesmes不相关,因为它甚至在根节点中...这两个命名空间让我感到困惑,我需要阅读一下背后的理论!谢谢! - The Surrican
1
每个元素都只属于一个命名空间,不管它的祖先在哪个命名空间中。在这个文档中,以 gesmes: 为前缀的节点属于一个命名空间,没有前缀的节点属于另一个(“默认”)命名空间。使用 XPath,你只需要指定你要查找的元素的命名空间,而不用考虑文档中包含的其他内容。 - IMSoP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接