用Python的minidom写XML

读、或者说解析XML的需求很常见,而写、或者说生成XML的需求就比较少见。 并且在很多情况下,用字符串手写、拼接,也非常的方便快捷。 然而,总有一些时候,需要用结构化的方式,去写一些复杂的XML。

经过挑选,孤选择了Python的minidom

XML简介

XML(eXtensible Markup Language,可扩展标记语言),是一种可用于任何类型的结构化数据的通用型语言。 它被设计来保存与传递数据,是一种W3C推荐的规范。 虽然形式与HTML类似,但XML没有任何预定义的Tag,具备很高的可扩展性。

XML Tree Structure

上图与以下代码,都源于XML Tutorial。 这个例子清晰地展示了XML对数据结构的表达。

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
  <book category="cooking">
    <title lang="en">Everyday Italian</title>
    <author>Giada De Laurentiis</author>
    <year>2005</year>
    <price>30.00</price>
  </book>
  <book category="children">
    <title lang="en">Harry Potter</title>
    <author>J K. Rowling</author>
    <year>2005</year>
    <price>29.99</price>
  </book>
  <book category="web">
    <title lang="en">Learning XML</title>
    <author>Erik T. Ray</author>
    <year>2003</year>
    <price>39.95</price>
  </book>
</bookstore>

概念

要想写XML,需要先知道基本概念。 因为工具都是根据概念来设计的,知道概念就很容易明白有哪些接口、分别是什么功能。

XML的核心概念可以用以下代码片表示。

<?xml version="1.0" encoding="UTF-8"?>
<!-- comment -->
<root>
    <element attribute="0">text</element>
    <sibling attribute="1">text</sibling>
</root>

由于是用工具写,而非手写,所以一些语法细节,可以不用关注。 详见XML Syntax

使用minidom

from xml.dom import minidom

写一个XML,大概过程是:

  1. 创建Document
  2. 创建element、定制attribute与text
  3. 输出Document

创建Document

xml = minidom.Document()

一个Document,其实就是一个XML文件。

创建root

root = xml.createElement('root')
xml.appendChild(root)

要注意两点:

  1. 需要使用appendChild,element才真的出现在Document中。
  2. 一个Document只能有一个root,Document级别的appendChild只能使用一次。

创建element

child = xml.createElement('child')
root.appendChild(child)

和root相比,普通element都是以root或其它element来调用appendChild

设置attribute

child.setAttribute('attr', '0')

attribute的值,必须是字符串。

设置text

node = xml.createTextNode('text')
child.appendChild(node)

text也是一个node,要像node一样去操作。

输出

minidom一共有两种输出方式,一是输出为字符串,二是输出到writer。 但归根结底,还是输出到writer。

    def toxml(self, encoding = None):
        return self.toprettyxml("", "", encoding)

    def toprettyxml(self, indent="\t", newl="\n", encoding = None):
        # indent = the indentation string to prepend, per level
        # newl = the newline string to append
        writer = _get_StringIO()
        if encoding is not None:
            import codecs
            # Can't use codecs.getwriter to preserve 2.0 compatibility
            writer = codecs.lookup(encoding)[3](writer)
        if self.nodeType == Node.DOCUMENT_NODE:
            # Can pass encoding only to document, to put it into XML header
            self.writexml(writer, "", indent, newl, encoding)
        else:
            self.writexml(writer, "", indent, newl)
        return writer.getvalue()

从以上源码中可以看出,其实toxml是用toprettyxml实现的,而toprettyxml则是用writexml来实现的。 输出时,可以指定XML的encoding,以及缩进、换行符。

>>> xml.toxml()
'<?xml version="1.0" ?><root><child attr="0">text</child></root>'
>>> print(xml.toprettyxml())
<?xml version="1.0" ?>
<root>
	<child attr="0">text</child>
</root>
>>> import sys
>>> xml.writexml(sys.stdout, '', ' ' * 4, '\n', 'UTF-8')
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <child attr="0">text</child>
</root>

可以看到,之前写的root、child等内容,都在其中。 需要打印到文件时,把上面writexml中的sys.stdout换成文件对象即可。

手写混合

如果需要混合一些既有的XML片段,到生成到一半的Document中,这时可以使用minidom提供的解析功能。

>>> hello = minidom.parseString('<hello>world</hello>')
>>> root.appendChild(hello.firstChild)
>>> xml.writexml(sys.stdout, '', ' ' * 4, '\n', 'UTF-8')
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <child attr="0">text</child>
    <hello>world</hello>
</root>

总结

本文只是简单介绍如何生成一个XML文件,相信以上内容就已经足够了。 通过minidom,也可以对既有的XML进行一些解析、查找、修改。


相关笔记