python - 为什么 ElementTree 吃/忽略命名空间(在属性值中)?

标签 python xml xsd namespaces elementtree

我正在尝试使用 ElementTree 读取 XML并将结果写回磁盘。我的长期目标是以这种方式美化 XML。然而,在我天真的方法中,ElementTree 吃掉了文档中的所有命名空间声明,我不明白为什么。这是一个例子
测试.xsd

<?xml version='1.0' encoding='UTF-8'?>
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
    xmlns='sdformat/pose' targetNamespace='sdformat/pose'
    xmlns:pose='sdformat/pose'
    xmlns:types='http://sdformat.org/schemas/types.xsd'>

<xs:import namespace='sdformat/pose' schemaLocation='./pose.xsd'/>

<xs:element name='pose' type='poseType' />

<xs:simpleType name='string'><xs:restriction base='xs:string' /></xs:simpleType>
<xs:simpleType name='pose'><xs:restriction base='types:pose' /></xs:simpleType>

<xs:complexType name='poseType'>
    <xs:simpleContent>
      <xs:extension base="pose">
    <xs:attribute name='relative_to' type='string' use='optional' default=''>
    </xs:attribute>

      </xs:extension>
    </xs:simpleContent>
</xs:complexType>


</xs:schema>
测试.py
from xml.etree import ElementTree

ElementTree.register_namespace("types", "http://sdformat.org/schemas/types.xsd")
ElementTree.register_namespace("pose", "sdformat/pose")
ElementTree.register_namespace("xs", "http://www.w3.org/2001/XMLSchema")

tree = ElementTree.parse("test.xsd")
tree.write("test_out.xsd")
生产 test_out.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="sdformat/pose">

<xs:import namespace="sdformat/pose" schemaLocation="./pose.xsd" />

<xs:element name="pose" type="poseType" />

<xs:simpleType name="string"><xs:restriction base="xs:string" /></xs:simpleType>
<xs:simpleType name="pose"><xs:restriction base="types:pose" /></xs:simpleType>

<xs:complexType name="poseType">
    <xs:simpleContent>
      <xs:extension base="pose">
    <xs:attribute name="relative_to" type="string" use="optional" default="">
    </xs:attribute>

      </xs:extension>
    </xs:simpleContent>
</xs:complexType>


</xs:schema>
注意如何 test_out.xsd 缺少来自 的任何命名空间声明测试.xsd .我希望它们是相同的。我通过验证来验证后者是有效的 XML。除了我选择的命名空间 URI 之外,它都会进行验证,我认为这无关紧要。

更新:
根据 mzji 的评论,我意识到这只发生在属性值上。考虑到这一点,我可以像这样手动添加命名空间:
from xml.etree import ElementTree

namespaces = {
    "types": "http://sdformat.org/schemas/types.xsd",
    "pose": "sdformat/pose",
    "xs": "http://www.w3.org/2001/XMLSchema"
}

for prefix, ns in namespaces.items():
    ElementTree.register_namespace(prefix, ns)

tree = ElementTree.parse("test.xsd")
root = tree.getroot()

queue = [tree.getroot()]
while queue:
    element:ElementTree.Element = queue.pop()
    for value in element.attrib.values():
        try:
            prefix, value = value.split(":")
        except ValueError:
            # no namespace, nothing to do
            pass
        else:
            if prefix == "xs":
                break  # ignore XMLSchema namespace
            root.attrib[f"xmlns:{prefix}"] = namespaces[prefix]

    for child in element:
        queue.append(child)

tree.write("test_out.xsd")
虽然这解决了问题,但这是一个相当丑陋的解决方案。我也仍然不明白为什么会发生这种情况,所以它没有回答这个问题。

最佳答案

这种行为是有正当理由的,但它需要对 XML Schema 概念有很好的理解。
首先,一些重要的事实:

  • 您的 XML 文档不仅仅是任何旧的 XML 文档。它是一个 XSD。
  • XSD 由模式描述(参见 schema for schema)
  • 属性 xs:restriction/@base 不是 xs:string。它的类型是 xs:QName。

  • 基于以上事实,我们可以断言:
  • 如果 test.xsd 被解析为 XML 文档,但不知道“模式模式”,则 base 的值属性将被视为字符串(从技术上讲,作为 PCDATA)。
  • 如果 test.xsd 是使用验证 XML 解析器解析的,使用“schema for schema”作为 XSD,则 base 的值属性将被解析为 xs:QName

  • 当 ElementTree 写入输出 XML 时,其行为应取决于 base 的数据类型.如 base是一个 QName 然后 ElementTree 应该检测到它正在使用命名空间前缀“types”并且它应该发出相应的命名空间声明。
    如果您在解析 test.xsd 时没有提供“schema for schema”,那么 ElementTree 就没有问题,因为它不可能知道 base应该被解释为 QName。

    关于python - 为什么 ElementTree 吃/忽略命名空间(在属性值中)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68524488/

    相关文章:

    java - jaxb 将随机字符串绑定(bind)到 xs :boolean 'true'

    python - 带有 --python 标志的 mkvirtualenv 使用了错误的 python 版本

    python - 使用 codecs.open() 访问内存中的解压文件

    python - matplotlib plt 中基于数据某些属性的多色图

    android - "No resource found that matches the given name"错误,即使名称匹配

    java - Jaxb UnMarshal 错误 : unexpected element (uri :"", 本地 :"processedSalesOrderTypeList")。预期的元素是

    java - 即使使用 ErrorHandler,为什么模式验证会在第一个错误后结束?

    python - Emacs:在评估缓冲区时启动一个新的劣质 python shell

    python - 如何使用 python lxml 获取 html 元素

    python - 在x节点minidom xml python之后插入