从 XML - Python 获取 xsi 类型

Get xsi type from XML - Python

我有以下 "test.xml" 文件:

<?xml version="1.0" encoding="UTF-8"?>
<test:myXML xmlns:test="http://com/my/namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Parent>
  <Child1 xsi:type="sample-type">
    <GrandChild1>123</GrandChild1>
    <GrandChild2>BranchName</GrandChild2>
  </Child1>
  <Child2 xsi:type="sample-type2"></Child2>
</Parent>
</test:myXML>

我想检索任何节点(如果存在)的 'xsi:type'。例如,在上面的 xml 中,我想遍历每个节点和 return "sample-type" 和 "sample-type2"

到目前为止,我得到了以下代码:

from lxml import etree

XMLDoc = etree.parse("test.xml")
rootXMLElement = XMLDoc.getroot()
tree = etree.parse("test.xml")

for Node in XMLDoc.xpath('//*'):
    if "xsi:type" in Node.attrib:
        #Do whatever

但是,这不起作用,因为结果中的 "xsi:type" 似乎被命名空间声明中的 xmlns:xsi 字面上替换了。例如,如果我使用以下代码打印每个节点属性:

from lxml import etree

XMLDoc = etree.parse("test.xml")
rootXMLElement = XMLDoc.getroot()
tree = etree.parse("test.xml")

for Node in XMLDoc.xpath('//*'):
    print(Node.attrib)

结果是:

{}
{}
{'{http://www.w3.org/2001/XMLSchema-instance}type': 'sample-type'}
{}
{}
{'{http://www.w3.org/2001/XMLSchema-instance}type': 'sample-type2'}

如您所见,在 "xsi-type" 属性存在的地方,它实际上将其替换为命名空间中的 xsi。 我怎样才能阻止这种情况发生?我想搜索 xsi 类型,而不是从命名空间声明中输入字符串文字。

xsi是命名空间前缀,不是命名空间。前缀唯一需要保持一致的地方是在声明它的 XML 元素内。

前缀甚至不需要在同一个 XML 文档中保持一致,您可以让同一个命名空间被同一个文档中任意数量的不同前缀引用。

尤其是 XML 文档和您的 XML 处理代码之间不必保持一致,您应该(阅读:必须)不编写假定前缀或依赖前缀的任何代码。

这就是 if "xsi:type" in Node.attrib: 没有意义的原因 - 它假定前缀必须是 xsixsi 可能常用于 http://www.w3.org/2001/XMLSchema-instance 命名空间,但这只是约定,并非保证。

XML文档可以写成

<test:myXML xmlns:test="http://com/my/namespace" xmlns:blah="http://www.w3.org/2001/XMLSchema-instance">
<Parent>
  <Child1 blah:type="sample-type">
    <GrandChild1>123</GrandChild1>
    <GrandChild2>BranchName</GrandChild2>
  </Child1>
  <Child2 blah:type="sample-type2"></Child2>
</Parent>
</test:myXML>

这将是完全一样的事情

这就是为什么 lxml 在显示节点时或在其 XPath 方言中使用名称空间 URI 而不是前缀的原因 - URI 很重要,前缀是短暂的。

您需要在程序中定义命名空间映射

nsmap = {
  'xsi': 'http://www.w3.org/2001/XMLSchema-instance'
}

并在您 select 命名空间中的节点时使用该映射 - 明确地:

if f"{{{nsmap['xsi']}}}type" in node.attrib:
    # ...

或通过 XPath

type = node.xpath('@xsi:type', nsmap)

这使您的程序独立于前缀 - 您可以自由使用任何您喜欢的前缀,XML 文档可以自由使用它喜欢的任何前缀,代码将以任何方式工作。


极端的例子,但对概括思路很有用:

<test:myXML xmlns:test="http://com/my/namespace" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Parent xmlns:blah="http://www.w3.org/2001/XMLSchema-instance">
    <Child1 foo:type="sample-type" xmlns:foo="http://www.w3.org/2001/XMLSchema-instance">
      <GrandChild1>123</GrandChild1>
      <GrandChild2>BranchName</GrandChild2>
    </Child1>
    <Child2 blah:type="sample-type2"></Child2>
  </Parent>
</test:myXML>

这里,http://www.w3.org/2001/XMLSchema-instance 得到 3 个前缀。 xsiblahfoo,每个都有不同的范围。

解析这个的时候,你会用哪一个来引用xsi?这有关系吗? 应该重要吗?不,不应该。需要匹配的只是名称空间 URI,我们一点都不关心 XML 文档对前缀做了什么:

nsmap = {
  's': 'http://www.w3.org/2001/XMLSchema-instance'
}

type = node.xpath('@s:type', namespaces=nsmap)