繁体   English   中英

如何让 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 的布局 - 它仍然是一团糟。

我还决定摆弄并尝试做:
tree = minidom.parseString(ET.tostring(root)).toprettyxml(indent = " ")
而不是将其打印到 Python shell,这会给出错误AttributeError: 'unicode' object has no attribute 'write'

问题

When I write my tree to an XML file on the last line, is there a way to pretty print to the XML file as it does to the Python shell?

我可以在这里使用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'))

我只是用indent()函数解决了它:

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

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

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

我找到了一种使用直接 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 中仍然有效。

安装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) 

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

将本安德森的答案视为一个函数。

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>

看看vkbeautify模块。

输入和输出可以是任何组合的字符串/文件。 它非常紧凑,没有任何依赖性。

import vkbeautify as vkb

a) pretty_text = vkb.xml(your_xml_text)  #return String   

b) vkb.xml(your_xml_text, 'path/to/dest/file') #save in file 

一个 liner(*) 从名为fname的文件中读取、解析(一次)和漂亮地打印 XML:

from xml.dom import minidom
print(minidom.parseString(open(fname).read()).toprettyxml(indent="  "))

(* 不包括进口)

使用纯 ElementTree 和 Python 3.9+:

def prettyPrint(element):
    encoding = 'UTF-8'
    # Create a copy of the input element: Convert to string, then parse again
    copy = ET.fromstring(ET.tostring(element))
    # Format copy. This needs Python 3.9+
    ET.indent(copy, space="    ", level=0)
    # tostring() returns a binary, so we need to decode it to get a string
    return ET.tostring(copy, encoding=encoding).decode(encoding)

如果您需要一个文件,请将最后一行替换为copy.write(...)以避免额外的开销。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM