参考 - 如何在SimpleXML中处理名称空间(带冒号的标签和属性)?

时间:2017-07-03 22:08:19

标签: php simplexml xml-namespaces

这个问题旨在作为回答一个特别常见问题的参考,这个问题可能有不同的形式:

  • 我有一个XML文档,其中包含多个名称空间;我如何用SimpleXML解析它?
  • 我的XML在标记名称中有一个冒号(“:”),如何使用SimpleXML访问它?
  • 如果名称中有冒号,我如何访问XML文件中的属性?

如果您的问题已与此相关,则可能与这些示例不同,但此页面应告诉您需要了解的内容。

以下是一个说明性示例:

$xml = '
    <?xml version="1.0" encoding="utf-8"?>
    <document xmlns="http://example.com" xmlns:ns2="https://namespaces.example.org/two" xmlns:seq="urn:example:sequences">
        <list type="short">
            <ns2:item seq:position="1">A thing</ns2:item>
            <ns2:item seq:position="2">Another thing</ns2:item>
        </list>
    </document>
';
$sx = simplexml_load_string($xml);

此代码无法使用;为什么不呢?

foreach ( $sx->list->ns2:item as $item ) {
    echo 'Position: ' . $item['seq:position'] . "\n";
    echo 'Item: ' . (string)$item . "\n";
}

第一个问题是->ns2:item语法无效;但将其更改为无效

foreach ( $sx->list->{'ns2:item'} as $item ) { ... }

为什么不呢,你应该用什么呢?

1 个答案:

答案 0 :(得分:8)

什么是XML命名空间?

标记或属性名称中的冒号(:)表示该元素或属性位于 XML命名空间中。命名空间是一种在一个文档中组合不同XML格式/标准的方法,并跟踪哪些名称来自哪种格式。冒号和它之前的部分实际上并不是标记/属性名称的一部分,它们只是指出它所在的命名空间。

XML命名空间有一个命名空间标识符,由URI(URL或URN)标识。 URI没有指向任何东西,它只是某人“拥有”命名空间的一种方式。例如,SOAP标准使用命名空间http://www.w3.org/2003/05/soap-envelope,OpenDocument文件使用(以及其他)urn:oasis:names:tc:opendocument:xmlns:meta:1.0。问题中的示例使用名称空间http://example.comhttps://namespaces.example.org/two

在文档或文档的某个部分中,命名空间被赋予本地前缀,这是您在冒号之前看到的部分。例如,在不同的文档中,SOAP名称空间可能被赋予本地前缀soap:SOAP:SOAP-ENV:env:或仅ns1:。这些名称使用特殊的xmlns属性链接回命名空间的标识符,例如xmlns:soap="http://www.w3.org/2003/05/soap-envelope"。在特定文档中选择前缀是完全随意的,并且可以在每次生成时改变而不改变含义。

最后,每个文档或文档的一部分都有一个默认命名空间,这是用于没有前缀的元素的命名空间。它由xmlns属性定义,没有:,例如xmlns="http://www.w3.org/2003/05/soap-envelope"。在上面的示例中,<list>位于默认命名空间中,定义为http://example.com

有点奇怪的是,未加前缀的属性永远不会出现在默认名称空间中,而是出现在一种“void namespace”中,标准没有明确定义。请参阅:XML Namespaces and Unprefixed Attributes

SimpleXML给了我一个空对象;怎么了?

如果在名称空间为的SimpleXML对象上使用print_rvar_dump或类似的“转储结构”函数,则不会显示某些内容。 它仍然存在,可以按照以下说明进行访问。

如何在SimpleXML中访问命名空间?

SimpleXML提供了两种使用命名空间的主要方法:

  • The ->children() method允许您访问特定命名空间中的子元素。它有效地切换您的对象以查看该命名空间,直到您再次调用它来切换回或另一个命名空间。
  • The ->attributes() method以类似的方式工作,但允许您访问特定命名空间中的属性

这两种方法都将名称空间标识符作为第一个参数。由于这些标识符相当长,因此定义一个常量或变量来表示您正在使用的命名空间会很有用,因此您无需在任何地方复制和粘贴完整的URI。

例如,上面的示例可能会变为:

define('XMLNS_EG2', 'https://namespaces.example.org/two');
define('XMLNS_SEQ', 'urn:example:sequences');
foreach ( $sx->list->children(XMLNS_EG2)->item as $item ) {
    echo 'Position: ' . $item->attributes(XMLNS_SEQ)->position . "\n";
    echo 'Item: ' . (string)$item . "\n";
}

作为简介,您还可以通过将第二个参数设为true来传递命名空间的本地别名方法。请记住,此前缀可能随时更改,例如,生成器可能会分配前缀ns1ns2等,如果代码稍有变化,则以不同的顺序分配它们。使用这个简写,代码将成为:

foreach ( $sx->list->children('ns2', true)->item as $item ) {
    echo 'Position: ' . $item->attributes('seq', true)->position . "\n";
    echo 'Item: ' . (string)$item . "\n";
}

(这个简介是在PHP 5.2中添加的,您可能会看到使用$sx->getNamespaces的更长篇版本的旧版本来获取前缀标识符对的列表。这是两者中最差的世界,因为你仍然在硬编码前缀而不是标识符。)