从XML获取xsi类型-Python

问题描述 投票:1回答:1

我有以下“ 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中,我想遍历每个节点并返回“ 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字面所取代。作为说明,如果我使用以下代码打印每个Node属性:

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类型,而不是从名称空间声明中输入字符串文字。

python xml lxml
1个回答
3
投票

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'
}

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

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)
© www.soinside.com 2019 - 2024. All rights reserved.