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

Kim*_*uey 30 python xml pretty-print elementtree python-2.6

背景

我正在使用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")
Run Code Online (Sandbox Code Playgroud)

尝试

我已经尝试使用tree.write("New_Database.xml", "utf-8")上面代码的最后一行,但它根本没有编辑XML的布局 - 它仍然是混乱的混乱.

我还决定摆弄并尝试做:而不是将其打印到Python shell,这给出了错误AttributeError:'unicode'对象没有属性'write'.
tree = minidom.parseString(ET.tostring(root)).toprettyxml(indent = " ")

问题

当我将我的树写到最后一行的XML文件时,是否有一种方法可以像在Python shell中那样打印到XML文件?

我可以toprettyxml()在这里使用,还是有不同的方法来做到这一点?

小智 100

我简单地用这个indent()函数解决了它:

xml.etree.ElementTree.indent(tree, space=" ", level=0)将空格附加到子树以在视觉上缩进树。这可用于生成打印精美的 XML 输出。树可以是ElementElementTreespace是为每个缩进级别插入的空白字符串,默认情况下为两个空格字符。要在已缩进的树内缩进部分子树,请将初始缩进级别传递为level

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

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

  • 值得一提的是,Python 3.9 中添加了“indent()”函数。 (34认同)
  • 你就是那个人。那个人。这绝对是最好的答案。 (8认同)
  • 请注意,@Tata​​rize 的答案实际上有一个适用于较旧 Python 版本的 polyfill。 (3认同)

Jon*_*ice 42

无论您的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)
Run Code Online (Sandbox Code Playgroud)

有一种可能的复杂性,特别是在Python 2中,它对字符串中的Unicode字符不太严格和不太复杂.如果您的toprettyxml方法u"something"提交了一个Unicode字符串(),那么您可能希望将其转换为合适的文件编码,例如UTF-8.例如,用以下内容替换一个写行:

f.write(xmlstr.encode('utf-8'))
Run Code Online (Sandbox Code Playgroud)

  • 如果您包含似乎需要的“import xml.dom.minidom as minidom”语句,这个答案会更清楚。 (4认同)
  • 这是关于如何将序列化的XML写入文件的答案,而不是对OP的序列化策略(无疑是拜占庭)的认可。我喜欢`lxml`,但由于它基于C语言,因此并不总是可用。 (3认同)

Tat*_*ize 16

将本·安德森的答案作为一个函数进行重复。

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))
Run Code Online (Sandbox Code Playgroud)

因此对不漂亮的数据运行测试:

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())
Run Code Online (Sandbox Code Playgroud)

我们得到:

<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>
Run Code Online (Sandbox Code Playgroud)

  • 与其他答案相比,该解决方案有几个很好的特点。它不需要额外的库;它适用于 3.9 之前的 Python,并且非常明确地将空白添加到树中的位置(这确实有助于理解当前的问题)。哦,它生成了与我手工制作的参考文件逐字节相同的 XML :-) (5认同)

小智 11

我找到了一种使用直接 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>
Run Code Online (Sandbox Code Playgroud)

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

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.
Run Code Online (Sandbox Code Playgroud)

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

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

<?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>
Run Code Online (Sandbox Code Playgroud)

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

这段代码是在 Python3.7 中编写的,但在 Python2.7 中仍然有效。

  • 如果您不必手动缩进它会很好。 (3认同)
  • 太棒了!这就是奉献! (2认同)

Nic*_*ick 8

如果要使用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)`
Run Code Online (Sandbox Code Playgroud)

如果您看到 xml 命名空间,例如py:pytype="TREE",您可能希望在创建之前添加xml_object

etree.cleanup_namespaces(root) 
Run Code Online (Sandbox Code Playgroud)

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

  • 让我看看我是否可以生成一个孤立的案例。但重点是我有一个基于 import xml.etree.ElementTree as ETree 的根,当我尝试您的建议时,我收到了一些错误消息。 (4认同)

RJX*_*RJX 5

安装 bs4

pip install bs4
Run Code Online (Sandbox Code Playgroud)

使用此代码可以漂亮地打印:

from bs4 import BeautifulSoup

x = your xml

print(BeautifulSoup(x, "xml").prettify())
Run Code Online (Sandbox Code Playgroud)

  • 当我们不想将 XML 写入文件时,这是一个很好的解决方案。 (2认同)
  • 当我尝试此操作时出现错误“无法找到具有您请求的功能的树生成器:xml。您需要安装解析器库吗?” 我有字符串格式的有效 XML。我还需要更多东西吗? (2认同)