如何(手动)编辑元素树的 xml 格式

How can (manually) edit the xml format of element tree

假设我有 2 个这样的 XML 文件:

version1.xml:

<object>
    <name>boat</name>
    <pose>Unspecified</pose>
    <truncated>0</truncated>
    <difficult>0</difficult>
    <bndbox>
        <xmin>0</xmin>
        <ymin>434</ymin>
        <xmax>152</xmax>
        <ymax>504</ymax>
    </bndbox>
</object>

version2.xml:

<object><name>boat</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox><xmin>0</xmin><ymin>434</ymin><xmax>152</xmax><ymax>504</ymax></bndbox></object>

它们之间的唯一区别是空格,这使得第一个更易于阅读。我正在尝试找到如何将第二种格式转换为第一种格式。

我知道我可以使用一些解决方法,比如使用这个 GitHub gist 中的这个函数(我在其他场合也有):

from xml.dom import minidom
from xml.etree import ElementTree


def prettify(elem):
    """
    Return a pretty-printed XML string for the Element.
    """
    rough_string = ElementTree.tostring(elem, 'utf-8')
    reparsed = minidom.parseString(rough_string)
    return reparsed.toprettyxml(indent='  ')

但我不是在寻找这个(我的需要涉及部分格式化的 XML,它不能完美地与上述功能一起使用)。

如果我检查每个元素的文本差异,我会得到:

import xml.etree.ElementTree as ET

xml_path1 = 'path/to/version1.xml'
xml_path2 = 'path/to/version2.xml'
tree1 = ET.parse(xml_path1)
annot1 = tree1.getroot()
tree2 = ET.parse(xml_path2)
annot2 = tree2.getroot()

for elem1, elem2 in zip(annot1.iter(), annot2.iter()):
    if repr(elem1.text) != repr(elem2.text):
        print(elem1, repr(elem1.text), elem2, repr(elem2.text))

输出:

<Element 'object' at 0x7fb26fc2d9f8> '\n\t' <Element 'object' at 0x7fb1c4e4da48> None
<Element 'bndbox' at 0x7fb1c4e4d8b8> '\n\t\t' <Element 'bndbox' at 0x7fb1c4e4dbd8> None

如果我将指定的文本更改为相应的 version1 文本,当然,它确实会将格式更改为受影响的元素,但它。

ET.dump(annot2)

输出:

<object>
    <name>boat</name><pose>Unspecified</pose><truncated>0</truncated><difficult>0</difficult><bndbox>
        <xmin>0</xmin><ymin>434</ymin><xmax>152</xmax><ymax>504</ymax></bndbox></object>

所需的输出当然是:

ET.dump(annot1)

输出:

<object>
    <name>boat</name>
    <pose>Unspecified</pose>
    <truncated>0</truncated>
    <difficult>0</difficult>
    <bndbox>
        <xmin>0</xmin>
        <ymin>434</ymin>
        <xmax>152</xmax>
        <ymax>504</ymax>
    </bndbox>
</object>

那么,格式化有什么用呢?我知道它实际上并不影响 XML 的内容(机器认为内容与我认为的相同)但是 Element Tree 保存此信息的位置让我望而却步。

缺少的空格在 Element.tail (docs) 中。

生成输出时,ElementTree 打印起始元素、内容、结束元素,然后是尾部。

这里有一个设置子树格式的技巧(并保留文档的其余部分):

  1. 使用上面的技巧漂亮地打印子树
  2. 转换为字符串
  3. 将字符串中的"\n"替换为"\n" + (" "*level),其中level是子树的深度。
  4. 用ETree将字符串解析成文档,并用新文档的根元素替换子树。

或者,您可以通过将子树包装在 level 包装器元素中来创建一个新文档,漂亮地打印整个文档,然后再次找到子树。