【问题标题】:Reference - How do I handle Namespaces (Tags and Attributes with a Colon in their Name) in SimpleXML?参考 - 如何在 SimpleXML 中处理命名空间(名称中带有冒号的标签和属性)?
【发布时间】:2021-05-03 17:30:14
【问题描述】:

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

  • 我有一个包含多个命名空间的 XML 文档;如何使用 SimpleXML 解析它?
  • 我的 XML 在标签名称中有一个冒号 (":"),我如何使用 SimpleXML 访问它?
  • 当 XML 文件中的属性名称中有冒号时,如何访问它们?

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

这是一个说明性示例:

$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>
    XML;
$sx = simplexml_load_string($xml);

此代码不起作用;为什么不呢?

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

第一个问题是-&gt;ns2:item是无效语法;但是把它改成这个也不行

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

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

【问题讨论】:

标签: php simplexml xml-namespaces


【解决方案1】:

什么是 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"。在上面的示例中,&lt;list&gt; 位于默认命名空间中,默认命名空间定义为 http://example.com

有点特别的是,无前缀属性从来不在默认命名空间中,而是在标准没有明确定义的一种“无效命名空间”中。见:XML Namespaces and Unprefixed Attributes

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

如果您在包含命名空间的 SimpleXML 对象上使用print_rvar_dump 或类似的“转储结构”函数,某些内容将不会显示。 它仍然存在,可以按如下所述进行访问。

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

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

  • The -&gt;children() method 允许您访问特定命名空间中的子元素。它有效地切换您的对象以查看该名称空间,直到您再次调用它以切换回或切换到另一个名称空间。
  • The -&gt;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 等,如果代码略有变化,则以不同的顺序分配它们。 依靠完整的命名空间 URI 始终是最好的方法

使用这个简写,代码会变成:

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-&gt;getNamespaces 来获取前缀标识符对的列表。这是两全其美的,因为您仍在硬编码前缀而不是标识符。)

【讨论】:

    【解决方案2】:

    在 XPath 中使用命名空间

    SimpleXML 具有an xpath() method,它允许您使用 XPath 1.0 语法搜索元素。要访问命名空间节点,您必须通过调用the registerXPathNamespace() method选择自己的前缀

    请记住,即使一个元素没有前缀和冒号,它也可以位于使用xmlns 声明的“默认命名空间”中。

    例如:

    define('XMLNS_EG2', 'https://namespaces.example.org/two');
    define('XMLNS_SEQ', 'urn:example:sequences');
    
    $sx->registerXPathNamespace('EG2', XMLNS_EG2);
    $sx->registerXPathNamespace('SEQ', XMLNS_SEQ);
    foreach ( $sx->xpath('//EG2:item[@SEQ:position=2]') as $item ) {
        echo 'Item: ' . (string)$item . "\n";
    }
    

    请注意,您选择的前缀不需要与 XML 中使用的匹配,它是您感兴趣的命名空间的本地别名

    还要注意registerXPathNamespacexpath 方法以外的任何东西都没有影响。如果您不使用 XPath,则需要使用 children()attributes(),如本页其他地方所述。

    限制

    • XPath 1.0 没有“默认命名空间”的概念(并且 SimpleXML 所基于的 XML 库不支持 XPath 2.0),因此您必须在每个元素和您想要的属性名称上使用前缀表示法匹配。
    • 注册的命名空间必须在您要调用xpath() 的特定对象上注册,并且不能继承或复制到其他对象。如果要根据不同的起点进行搜索,则每次都必须运行registerXPathNamespace

    【讨论】:

      猜你喜欢
      • 2011-03-01
      • 2014-06-15
      • 1970-01-01
      • 1970-01-01
      • 2011-03-02
      相关资源
      最近更新 更多