暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

读写系列(四):对XML的读写(中)

麟十一 2021-07-29
765

 第 23 篇  |  LINSHIYI 

写入XML


xml库中DOMElementTree两种方法都可以创建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>

第二步给子节点添加文本属性。在#2.1.3中,我们使用createTextNode()方法创建单独的文本节点,并使用appendChild()方法将文本节点追加进相应的子节点中,完成文本内容的写入。#2.1.4中使用方法setAttribute(key, value)给节点添加相应的属性。

#2.1.5将子节点追加进父结点中,并将根节点追加入文档对象DOM中,完成整棵文档树的构建。这里的方法toprettyxml()可以返回一个带缩进格式的XML字符串,此方法仅在调试时使用:

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>

#2.1.5中我们已经构建好了整个文档树,最后只需要使用writexml()方法将文档树DOM写入XML即可。介绍一下writexml()的5个参数:

Node.writexml(writer, indent="", addindent="", newl="", encoding=None)


writer指的是编写器对象(writer object),即我们打开的"write.xml"文件,这里被命名为f(名字随意)。indent指的是每一个开始标签前填充的内容,默认不填充。addindent代表每一个子节点的缩进字符,默认子节点不缩进。newl每个标签后的填充字符,默认不填充,encoding负责生成XML信息头中的encoding值,默认无。

我们先来看前4个参数具体代表什么含义,我在#2.1.6.1中使用默认参数生成XML,#2.1.6.2中设置indent="a",#2.1.6.3设置addindent="a",#2.1.6.4设置newl="a":

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")


#2.1.6.7中我们将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


使用ElementTree方法创建XML的步骤与DOM有所不同:先构建单独的节点并添加文本和属性,之后将子节点追加进父节点中,父节点追加进根节点中,最后将根节点转化为ElementTree对象,写入XML文件。

来看第一步,创建节点并添加文本和属性

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)

#2.2.1中我们使用Element()方法创建了4个独立的节点,使用SubElement()方法为节点content_node创建了一个子节点category_node。#2.2.2采用赋值的方式为节点添加文本内容,#2.2.3使用set(key, value)方法给节点添加属性。同样,这里的属性值文本都必须是字符串形式,否则会报错:

文本或属性值出现数字时的报错

这里的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, encodingxml_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>&#40607;&#21313;&#19968;</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>


不过这样输出的XML只有一行,如果想要设置缩进的话需要用到Python版本3.9中新增的函数indent(),在我使用的3.7.1中没有这个函数。

刚刚提到了ascii和Unicode编码,有兴趣的同学可以看一看这两篇文详细了解一下字符编码的内容 Unicode和它的朋友们(上) Unicode和它的朋友们(下)

4.3 总结和对比

DOM方法需要先创建文档对象,之后建立独立节点并追加进上级节点中,最后将根节点追加进文档对象中并写入XML。而ElementTree方法是先建立节点元素并进行追加,最后将根元素转换成ElementTree对象并写入XML中。

DOM方法可以轻松实现XML的换行和缩进,获得格式清晰的XML文件,而ElementTree在Python3.9中才新增了实现换行和缩进的函数,低版本的话需要自定义函数实现文件换行和缩进。


以上就是写入XML的全部内容,然后还有一个下篇 主要总结了对XML进行增删改的方法,欢迎继续收看


~END~

文章转载自麟十一,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论