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

Reference - How do I handle Namespaces (Tags and Attributes with a Colon in their Name) in SimpleXML?

提问人:IMSoP 提问时间:7/4/2017 最后编辑:IMSoP 更新时间:8/19/2022 访问量:5149

问:

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

  • 我有一个包含多个命名空间的 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";
}

第一个问题是语法无效;但是将其更改为此也不起作用->ns2:item

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

为什么不呢,你应该用什么来代替?

php simplexml xml 命名空间

评论

0赞 IMSoP 7/4/2017
相关元讨论:meta.stackoverflow.com/questions/351420/...

答:

25赞 7 revsIMSoP #1

什么是 XML 命名空间?

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

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

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

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

有点奇怪的是,无前缀属性从来不在默认命名空间中,而是在一种“void 命名空间”中,标准没有明确定义。请参见: XML 命名空间和无前缀属性

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

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

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

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

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

例如,上面的示例可能变成:

define('XMLNS_EG1', 'http://example.com');
define('XMLNS_EG2', 'https://namespaces.example.org/two');
define('XMLNS_SEQ', 'urn:example:sequences');

foreach ( $sx->children(XMLNS_EG1)->list->children(XMLNS_EG2)->item as $item ) {
    echo 'Position: ' . $item->attributes(XMLNS_SEQ)->position . "\n";
    echo 'Item: ' . (string)$item . "\n";
}

还可以在首次解析 XML 时使用参数选择初始命名空间,该参数是 、 或 的第四个参数。$namespace_or_prefixsimplexml_load_stringsimplexml_load_filenew SimpleXMLElement

例如,如果我们以这种方式创建对象,则不需要调用来访问元素:->children(XMLNS_EG1)list

$sx = simplexml_load_string($xml, null, 0, XMLNS_EG1);

(请注意,如果根元素使用默认命名空间而不是前缀,则 SimpleXML 将自动选择它;但由于无法预测将来哪个命名空间将成为默认命名空间,因此最好始终包含参数或初始调用。$namespace_or_prefix->children()

速记(不推荐)

作为简写,您还可以通过将第二个参数指定为 来向方法传递命名空间的本地别名。请记住,此前缀可能随时更改,例如,生成器可能会分配前缀 、 等,如果代码略有变化,则以不同的顺序分配它们。依赖完整的命名空间 URI 始终是最佳方法truens1ns2

使用此简写,代码将变为:

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

3赞 4 revs, 2 users 98%IMSoP #2

将命名空间与 XPath 一起使用

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

请记住,即使元素没有前缀和冒号,它也可以位于用 声明的“默认命名空间”中。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 中使用的前缀匹配,它是您感兴趣的命名空间的本地别名

另请注意,除了方法之外,这对其他任何内容都没有影响。如果您不使用 XPath,则需要使用 和 ,如本页其他地方所述。registerXPathNamespacexpathchildren()attributes()

局限性

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