如何让 Python 的 ElementTree 漂亮地打印到 XML 文件?

How do I get Python's ElementTree to pretty print to an XML file?

背景

我正在使用 SQLite 访问数据库并检索所需信息。我在 Python 2.6 版中使用 ElementTree 来创建包含该信息的 XML 文件。

代码

import sqlite3
import xml.etree.ElementTree as ET

# NOTE: Omitted code where I acccess the database,
# pull data, and add elements to the tree

tree = ET.ElementTree(root)

# Pretty printing to Python shell for testing purposes
from xml.dom import minidom
print minidom.parseString(ET.tostring(root)).toprettyxml(indent = "   ")

#######  Here lies my problem  #######
tree.write("New_Database.xml")

尝试次数

我试过使用 tree.write("New_Database.xml", "utf-8") 代替上面最后一行代码,但它根本没有编辑 XML 的布局 - 它仍然是一团糟。

我也决定 fiddle 并尝试这样做:
tree = minidom.parseString(ET.tostring(root)).toprettyxml(indent = " ")<br> 而不是打印到 Python shell,给出错误 AttributeError: 'unicode' object has no attribute 'write'.

问题

当我在最后一行将我的树写入 XML 文件时,有没有一种方法可以像打印 Python [=] 一样漂亮地打印到 XML 文件47=]?

我可以在这里使用 toprettyxml() 还是有其他方法可以做到这一点?

无论您的 XML 字符串是什么,您都可以通过打开用于写入的文件并将字符串写入文件来将其写入您选择的文件。

from xml.dom import minidom

xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(indent="   ")
with open("New_Database.xml", "w") as f:
    f.write(xmlstr)

可能存在一种复杂情况,尤其是在 Python 2 中,它对字符串中的 Unicode 字符既不严格也不复杂。如果您的 toprettyxml 方法返回一个 Unicode 字符串 (u"something"),那么您可能希望将其转换为合适的文件编码,例如 UTF-8。例如。将一行替换为:

f.write(xmlstr.encode('utf-8'))

安装bs4

pip install bs4

使用此代码进行漂亮的打印:

from bs4 import BeautifulSoup

x = your xml

print(BeautifulSoup(x, "xml").prettify())

如果想使用lxml,可以通过以下方式实现:

from lxml import etree

xml_object = etree.tostring(root,
                            pretty_print=True,
                            xml_declaration=True,
                            encoding='UTF-8')

with open("xmlfile.xml", "wb") as writter:
    writter.write(xml_object)`

如果您看到 xml 个名称空间,例如py:pytype="TREE",创建前可能要添加 xml_object

etree.cleanup_namespaces(root) 

这对于您的代码中的任何改编应该足够了。

我找到了直接使用 ElementTree 的方法,但它相当复杂。

ElementTree 具有编辑元素文本和尾部的功能,例如element.text="text"element.tail="tail"。你必须以特定的方式使用它们来让事情对齐,所以确保你知道你的转义字符。

作为一个基本示例:

我有以下文件:

<?xml version='1.0' encoding='utf-8'?>
<root>
    <data version="1">
        <data>76939</data>
    </data>
    <data version="2">
        <data>266720</data>
        <newdata>3569</newdata>
    </data>
</root>

要放置第三个元素并保持美观,您需要以下代码:

addElement = ET.Element("data")             # Make a new element
addElement.set("version", "3")              # Set the element's attribute
addElement.tail = "\n"                      # Edit the element's tail
addElement.text = "\n\t\t"                  # Edit the element's text
newData = ET.SubElement(addElement, "data") # Make a subelement and attach it to our element
newData.tail = "\n\t"                       # Edit the subelement's tail
newData.text = "5431"                       # Edit the subelement's text
root[-1].tail = "\n\t"                      # Edit the previous element's tail, so that our new element is properly placed
root.append(addElement)                     # Add the element to the tree.

要缩进内部标签(如内部数据标签),您必须将其添加到父元素的文本中。如果你想在一个元素之后(通常在子元素之后)缩进任何东西,你把它放在尾部。

当您将此代码写入文件时,此代码会给出以下结果:

<?xml version='1.0' encoding='utf-8'?>
<root>
    <data version="1">
        <data>76939</data>
    </data>
    <data version="2">
        <data>266720</data>
        <newdata>3569</newdata>
    </data> <!--root[-1].tail-->
    <data version="3"> <!--addElement's text-->
        <data>5431</data> <!--newData's tail-->
    </data> <!--addElement's tail-->
</root>

另请注意,如果您希望程序统一使用\t,您可能需要先将文件解析为字符串,并将所有缩进的空格替换为\t .

此代码是在 Python3.7 中编写的,但在 Python2.7.

中仍然有效

将 Ben Anderson 的答案作为函数进行重复。

def _pretty_print(current, parent=None, index=-1, depth=0):
    for i, node in enumerate(current):
        _pretty_print(node, current, i, depth + 1)
    if parent is not None:
        if index == 0:
            parent.text = '\n' + ('\t' * depth)
        else:
            parent[index - 1].tail = '\n' + ('\t' * depth)
        if index == len(parent) - 1:
            current.tail = '\n' + ('\t' * (depth - 1))

所以运行对不美观数据的测试:

import xml.etree.ElementTree as ET
root = ET.fromstring('''<?xml version='1.0' encoding='utf-8'?>
<root>
    <data version="1"><data>76939</data>
</data><data version="2">
        <data>266720</data><newdata>3569</newdata>
    </data> <!--root[-1].tail-->
    <data version="3"> <!--addElement's text-->
<data>5431</data> <!--newData's tail-->
    </data> <!--addElement's tail-->
</root>
''')
_pretty_print(root)

tree = ET.ElementTree(root)
tree.write("pretty.xml")
with open("pretty.xml", 'r') as f:
    print(f.read())

我们得到:

<root>
    <data version="1">
        <data>76939</data>
    </data>
    <data version="2">
        <data>266720</data>
        <newdata>3569</newdata>
    </data>
    <data version="3">
        <data>5431</data>
    </data>
</root>

我简单的用indent()函数解决了:

xml.etree.ElementTree.indent(tree, space=" ", level=0) Appends whitespace to the subtree to indent the tree visually. This can be used to generate pretty-printed XML output. tree can be an Element or ElementTree. space is the whitespace string that will be inserted for each indentation level, two space characters by default. For indenting partial subtrees inside of an already indented tree, pass the initial indentation level as level.

tree = ET.ElementTree(root)
ET.indent(tree, space="\t", level=0)
tree.write(file_name, encoding="utf-8")

注意,indent() 函数是在 Python 3.9 中添加的。

从名为 fname:

的文件中读取、解析(一次)并漂亮打印 XML 的一个衬里 (*)
from xml.dom import minidom
print(minidom.parseString(open(fname).read()).toprettyxml(indent="  "))

(*不包括进口)