
第 23 篇 | LINSHIYI 

xml库中DOM和ElementTree两种方法都可以创建XML文件,下面分别进行介绍:
4.1 DOM(Document Object Model)
使用DOM方法生成XML文件的步骤大致是这样的:首先创建一个文档对象(Document Object),后续生成节点和写入XML操作都需要基于这个文档对象。之后生成单独的根节点、子节点,并给相应的节点添加文本和属性,再之后将子节点追加进父节点中,父节点追加进根节点中,最后将根节点追加在文档对象中,将文档对象写入XML。
我们先来看第一步,创建文档对象和单独的节点:
1#2.写入XML
2#2.1 DOM方法
3import xml.dom.minidom
4#2.1.1 创建文档对象
5DOM = xml.dom.minidom.Document()
6print(DOM)
7#2.1.2 创建根节点和子节点
8root = DOM.createElement("example") #根节点
9name_node = DOM.createElement("name") #子节点1
10total_num_node = DOM.createElement("total_num") #子节点2
11content_node = DOM.createElement("content") #子节点3
12category_node = DOM.createElement("category") #子节点4
13print(root)
14print(name_node)
我们在#2.1.1中创建了一个文档对象DOM,并在#2.1.2中使用createElement()方法创建了5个节点,包括1个根节点和4个子节点。从#2.1.2的结果中可以看出我们创建的根节点和子节点都是DOM元素:
1#2.写入XML
2#2.1 DOM方法
3#2.1.1 创建文档对象
4<xml.dom.minidom.Document object at 0x000001BAA1CB2DC8>
5#2.1.2 创建根节点和子节点
6<DOM Element: example at 0x1baa14f5800>
7<DOM Element: name at 0x1baa11da768>
1#2.1.3 给子节点添加文本节点
2name_node.appendChild(DOM.createTextNode("麟十一"))
3total_num_node.appendChild(DOM.createTextNode("20"))
4category_node.appendChild(DOM.createTextNode("Python"))
5print(total_num_node.toprettyxml())
6#2.1.4 给子节点content添加属性
7content_node.setAttribute("id", "1")
8print(content_node.toprettyxml())
9#2.1.5 将节点都添加进文档对象中
10content_node.appendChild(category_node) # category --> content
11root.appendChild(name_node) # name --> root
12root.appendChild(total_num_node) # total_num --> root
13root.appendChild(content_node) # content --> root
14DOM.appendChild(root) # root --> DOM
15print(DOM.toprettyxml())
在这里还需要注意一点,无论是文本还是属性值,都需要传入字符串格式的数据(如这里的"20"和"1"),如果出现了数值型的数据,在写入XML时会报错:

文本出现数字时的报错

属性值出现数字时的报错
1#2.1.3 给子节点添加文本节点
2<total_num>20</total_num>
3#2.1.4 给子节点content添加属性
4<content id="1"/>
5#2.1.5 将节点都添加进文档对象中
6<?xml version="1.0" ?>
7<example>
8 <name>麟十一</name>
9 <total_num>20</total_num>
10 <content id="1">
11 <category>Python</category>
12 </content>
13</example>
Node.writexml(writer, indent="", addindent="", newl="", encoding=None)
1#2.1.6 将文档对象写入XML文件
2with open("write.xml", "w", encoding="utf-8") as f:
3 #2.1.6.1
4 DOM.writexml(f)
5 #2.1.6.2
6 DOM.writexml(f, indent="a")
7 #2.1.6.3
8 DOM.writexml(f, addindent="a")
9 #2.1.6.4
10 DOM.writexml(f, newl="a")
具体结果如下所示,#2.1.6.1使用默认参数生成的XML只有一行,且每个标签之间没有缩进和空格。#2.1.6.2中每个开始标签前都增加了一个字符串"a"。#2.1.6.3中每一个根节点直属的子节点(name,total_num,content)前都增加了一个"a",而二级子节点(category)前增加了两个"a"。#2.1.6.4中每一个标签之后都增加了一个字符串"a":
1<!--2.1.6 将文档对象写入XML文件-->
2<!--2.1.6.1-->
3<?xml version="1.0" ?><example><name>麟十一</name><total_num>20</total_num><content id="1"><category>Python</category></content></example>
4<!--2.1.6.2-->
5<?xml version="1.0" ?>a<example>a<name>麟十一</name>a<total_num>20</total_num>a<content id="1">a<category>Python</category>a</content>a</example>
6<!--2.1.6.3-->
7<?xml version="1.0" ?><example>a<name>麟十一</name>a<total_num>20</total_num>a<content id="1">aa<category>Python</category>a</content></example>
8<!--2.1.6.4-->
9<?xml version="1.0" ?>a<example>a<name>麟十一</name>a<total_num>20</total_num>a<content id="1">a<category>Python</category>a</content>a</example>a
我们目前获得的结果都只有一行,看起来不美观也不方便,结合这几个参数,有两种方法可以生成格式美观的XML:#2.1.6.5将indent设置为"\n",将addindent设置为"\t",即每一个节点前都会有换行符而且每个节点都会缩进。#2.1.6.6将addindent设置为"\t",将newl设置为"\n",意味着每个标签后会有换行符,子节点也会相应缩进:
1#2.1.6 将文档对象写入XML文件
2with open("write.xml", "w", encoding="utf-8") as f:
3 #2.1.6.5
4 DOM.writexml(f, indent="\n", addindent="\t")
5 #2.1.6.6
6 DOM.writexml(f, addindent="\t", newl="\n")
两种方法生成的XML是一样的,只不过#2.1.6.6的文档末尾多了一行空行,是因为newl参数会在标签之后填充内容,所以标签</example>后被填充了一个换行符"\n":
1<!--2.1.6 将文档对象写入XML文件-->
2<!--2.1.6.5-->
3<?xml version="1.0" ?>
4<example>
5 <name>麟十一</name>
6 <total_num>20</total_num>
7 <content id="1">
8 <category>Python</category>
9 </content>
10</example>
11<!--2.1.6.6-->
12<?xml version="1.0" ?>
13<example>
14 <name>麟十一</name>
15 <total_num>20</total_num>
16 <content id="1">
17 <category>Python</category>
18 </content>
19</example>
20
最后说一说参数encoding,我们会发现在这里出现了两个encoding,一个是使用with open语句打开文件时输入的参数,一个是writexml()中的参数,这两个encoding的含义是不同的。
在使用with open语句打开文件写入XML时设置的encoding="utf-8"制定了该文本的编码方式,而writexml()函数中的encoding只是在XML信息头中写入一串语句,目的是告诉他人此文件的编码方式,参数本身不会对文件进行编码。
1#2.1.6 将文档对象写入XML文件
2with open("write.xml", "w", encoding="utf-8") as f:
3 #2.1.6.7
4 DOM.writexml(f, indent="\n", addindent="\t", encoding="utf-8")
1<!--2.1.6 将文档对象写入XML文件-->
2<!--2.1.6.7-->
3<?xml version="1.0" encoding="utf-8"?>
4<example>
5 <name>麟十一</name>
6 <total_num>20</total_num>
7 <content id="1">
8 <category>Python</category>
9 </content>
10</example>
4.2 ElementTree
1#2.2 ElementTree方法
2from xml.etree import ElementTree as ET
3#2.2.1 创建根节点和子节点
4root = ET.Element("example") #根节点
5name_node = ET.Element("name") #子节点1
6total_num_node = ET.Element("total_num") #子节点2
7content_node = ET.Element("content") #子节点3
8category_node = ET.SubElement(content_node, "category") #子节点4
9print(root)
10print(name_node)
11#2.2.2 给子节点添加文本节点
12name_node.text = "麟十一"
13total_num_node.text = "20"
14category_node.text = "Python"
15ET.dump(name_node)
16#2.2.3 给子节点content添加属性
17content_node.set("id", "1")
18ET.dump(content_node)

这里的dump()方法可以输出元素树的结构或内容,仅供调试时使用。
1#2.2 ElementTree方法
2#2.2.1 创建根节点和子节点
3<Element 'example' at 0x000001BAA32FE598>
4<Element 'name' at 0x000001BAA32FE188>
5#2.2.2 给子节点添加文本节点
6<name>麟十一</name>
7#2.2.3 给子节点content添加属性
8<content id="1"><category>Python</category></content>
从#2.2.3中我们可以看到节点category_node已经被添加进了节点content_node中。除去SubElement()方法,我们还有append()和extend()两种方法可以追加节点,具体见#2.2.4。
在将所有节点都追加进根节点root中后,我们需要使用ElementTree()方法将根元素转化为ElementTree对象,为后续的写入XML做准备:
1#2.2.4 将子节点追加进根节点中
2root.append(name_node) #name_node --> root
3root.extend((total_num_node, content_node)) #(total_num, content) --> root
4ET.dump(root)
5#2.2.5 将根元素转化为ElementTree对象
6ElementTree = ET.ElementTree(root)
7print(ElementTree)
从#2.2.4的结果可以看到,所有的元素都已经追加进了根元素root中。append()每次可以追加一个节点,extend()可以追加多个,但是多个节点需要用括号括起来一起传入才可以。#2.2.5中生成的ElementTree是一个对象:
1#2.2.4 将子节点追进根节点中
2<example><name>麟十一</name><total_num>20</total_num><content id="1"><category>Python</category></content></example>
3#2.2.5 将根元素转化为ElementTree对象
4<xml.etree.ElementTree.ElementTree object at 0x0000025BFB73FCC0>
最后,我们进行XML写入操作,ElementTree模块提供了专门的写入函数write(),其中常用参数有file, encoding和xml_declaration。file代表文件名称,encoding是文件编码方式,默认us-ascii编码,xml_declaration是命名空间声明,默认为None。分别来看一下具体的参数使用:
1#2.2.6 将ElementTree对象写入XML文件
2#2.2.6.1
3ElementTree.write('write.xml')
4#2.2.6.2
5ElementTree.write('write.xml', encoding='utf-8')
6#2.2.6.3
7ElementTree.write('write.xml', encoding='utf-8', xml_declaration=True)
三种方法生成的文件结果如下。#2.2.6.1中全部使用默认参数生成XML文件,发现汉字“麟十一”被转化成了Unicode编码输出。#2.2.6.2中指定了文件的编码方式,汉字正常显示。#2.2.6.3中设置了XML的声明,我们发现生成的XML文件第一行自动加上了XML版本和相应的编码方式:
1<!--2.2.6 将ElementTree对象写入XML文件-->
2<!--2.2.6.1-->
3<example><name>麟十一</name><total_num>20</total_num><content id="1"><category>Python</category></content></example>
4<!--2.2.6.2-->
5<example><name>麟十一</name><total_num>20</total_num><content id="1"><category>Python</category></content></example>
6<!--2.2.6.3-->
7<?xml version='1.0' encoding='utf-8'?>
8<example><name>麟十一</name><total_num>20</total_num><content id="1"><category>Python</category></content></example>
Unicode和它的朋友们(上)
Unicode和它的朋友们(下)
主要总结了对XML进行增删改的方法,欢迎继续收看
END
~




