在 Python 中写入 .xml 并带有漂亮的打印和编码声明

Write .xml in Python with pretty print and encoding declaration

我必须创建一个 .xml 文件,其中包含漂亮的打印和编码声明。它应该看起来像这样:像这样:

<?xml version='1.0' encoding='utf-8'?>
<main>
    <sub>
        <name>Ana</name>
        <detail />
        <type>smart</type>
    </sub>
</main>

我知道如何获得漂亮的印刷品和声明,但不是同时获得。 要获得 UTF-8 声明,但没有漂亮的打印,我使用下面的代码:

f = open(xmlPath, "w")
et.write(f, encoding='utf-8', xml_declaration=True) 
f.close()

但是如果我想得到漂亮的打印,我必须将xml树转换成字符串,我会丢失声明。我使用此代码:

from xml.dom import minidom
xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(indent="   ")
with open(xmlPath, "w") as f:
    f.write(xmlstr.encode('utf-8'))
    f.close()

使用最后一个代码,我得到了漂亮的打印,只是第一行是:

<?xml version="1.0" ?>

我不妨将其替换为

<?xml version='1.0' encoding='utf-8'?>

但我不认为这是最符合 Python 风格的方法。

我使用 xml 模块,我不想安装额外的模块,因为脚本必须来自具有标准 Python 的各种计算机 运行。但如果不行的话,我会安装其他模块。

稍后编辑:

最后,在 Lenz 的帮助下,我使用了这个:

#ET=lxml.etree
xmlPath=os.path.join(output_folderXML ,"test.xml")
xmlstr= ET.tostring(root, encoding='UTF-8', xml_declaration=True, pretty_print=True)
with open(xmlPath, "w") as f:
    f.write(xmlstr)
    f.close()

我需要知道在 "w" 模式下将 "tostring" 方法的结果写入 .xml 文件是否安全,而不是 "wb"。 正如我在下面的一条评论中所说,使用 "wb" 在记事本中打开 xml 文件时,我没有得到漂亮的打印,但是使用 "w",我可以。 另外,我检查了以"w"模式编写的xml文件,其中有“ü”等特殊字符。 我只需要一个合格的意见,我所做的在技术上是可以的。

最优雅的解决方案当然是使用第三方库 lxml,它被广泛使用——有充分的理由。 它在 tostring() 方法中同时提供 pretty_printxml_declaration 参数,因此您可以同时获得两者。 API 与您现在似乎正在使用的 std-lib ElementTree 非常接近。这是一个例子:

>>> from lxml import etree
>>> doc = etree.parse(xmlPath)
>>> print etree.tostring(doc, encoding='UTF-8', xml_declaration=True,
                         pretty_print=True)
<?xml version='1.0' encoding='UTF-8'?>
<main>
  <sub>
    <name>Ana</name>
    <detail/>
    <type>smart</type>
  </sub>
</main>

不过,我理解您只想使用 "included batteries"。 据我所知,xml.etree.ElementTree 无法自动更改缩进。 但是 minidom 变通方法有一个同时获得漂亮打印和完整声明的解决方案:使用 toprettyxml() 方法的 encoding 参数!

>>> doc = minidom.parseString(ET.tostring(root))
>>> print doc.toprettyxml(encoding='utf8')
<?xml version="1.0" encoding="utf8"?>
<main>
    <sub>
        <name>Ana</name>
        <detail/>
        <type>smart</type>
    </sub>
</main>

(请注意,返回的字符串已经过编码,您应该将其写入以二进制模式 ("wb") 打开的文件,无需进一步编码。)

from xml.dom import minidom
xmlstr = minidom.parseString(ET.tostring(root)).toprettyxml(indent="   ", encoding='UTF-8')
with open(xmlPath, "w") as f:
    f.write(str(xmlstr.decode('UTF-8')))
    f.close()

这可能会在不使用 lxml 等外部库的情况下解决您的问题

经过一番努力并阅读了大量丑陋的代码后,我想出了这个简单但又 使用 lxmllybrary 的 E-Factory 编写缩进 XML 文件的有效解决方案。

此解决方案是提供的其他解决方案的集合,但使用 E-Factory 实现,对谁来说它更具可读性和 Pythonic

from lxml import etree, builder
E = builder.ElementMaker()

the_doc = E.root(
        E.data(
            E.field1('Text...', name='field.one.name', id="field-id"),
            E.field2('Text...', name='field.two.name', id="field-id"),
            E.field3(
                E.subfield1('Text...', name='subfield.one.name', id="field-id"),
                E.subfield2('Text...', name='subfield.two.name', id="field-id"),
            )
            )
        )

# Handling the Pretty print 
pprinted_xml = etree.tostring(the_doc, encoding='UTF-8', xml_declaration=True,
                         pretty_print=True)
# Creating the XML file
with open('test.xml', 'wb') as f:
    f.write(pprinted_xml)

结果

<?xml version='1.0' encoding='UTF-8'?>
<root>
  <data>
    <field1 name="field.one.name" id="field-id">Text...</field1>
    <field2 name="field.two.name" id="field-id">Text...</field2>
    <field3>
      <subfield1 name="subfield.one.name" id="field-id">Text...</subfield1>
      <subfield2 name="subfield.two.name" id="field-id">Text...</subfield2>
    </field3>
  </data>
</root>