14. 文档处理 XML
![]() |
我们考虑以下文档 XML:
<tribu>
<enseignant>
<personne sexe="M">
<nom>dupont</nom>
<prenom>jean</prenom>
<age>28</age>
ceci est un commentaire
</personne>
<section>27</section>
</enseignant>
<etudiant>
<personne sexe="F">
<nom>martin</nom>
<prenom>charline</prenom>
<age>22</age>
</personne>
<formation>dess IAIE</formation>
</etudiant>
</tribu>
我们分析此文档以生成以下控制台输出:
tribu
enseignant
(personne,(sexe,M) )
nom
[dupont]
/nom
prenom
[jean]
/prenom
age
[28]
/age
/personne
section
[27]
/section
/enseignant
etudiant
(personne,(sexe,F) )
nom
[martin]
/nom
prenom
[charline]
/prenom
age
[22]
/age
/personne
formation
[dess IAIE]
/formation
/etudiant
/tribu
我们需要知道如何识别:
- 一个开始标签,例如 <formation>;
- 结束标签,例如 </enseignant>;
- 带有属性的开始标签,例如 <person sex="F">;
- 标签主体,例如 martin 中的 <nom>martin</nom>.
用于分析代码 XML 的程序被称为 XML 解析器。 有两个模块为我们提供了分析代码 XML 的功能:xml.sax 和 xml.sax.handler。
[xml.sax] 模块通过以下语句为我们提供了一个 XML 解析器:
该解析器按顺序分析文本 XML。它会在以下事件发生时调用用户的方法:
- 在标签开始时调用方法 startElement;
- 在标签结束时调用方法 endElement;
- 在标签主体处调用方法 characters。
我们需要向解析器指定实现这些方法的类:
我们将实现方法 startElement、endElement、characters 的类实例传递给解析器的 setContentHandler 方法。 所使用的类是 xml.sax.handler.ContentHandler 类的派生类。上述方法通过以下参数被调用:
- name 是起始标签的名称。attributs 是该标签的属性字典。 因此,对于标签 <personne sexe="M">,将得到 name="personne" 和 attributs={'sexe':'M'}
- name 是结束标签的名称。因此,对于标签 </etudiant>,对应的结束标签为 name='etudiant'。
- data 是标签的正文。因此,如果标签是
我们将得到 data='\r\n dupont\r\n '。通常情况下,我们会去除数据前后多余的空格。
说明完毕,接下来我们可以进入 XML 文档的解析脚本:
该程序(xml_sax_01)
# -*- coding=utf-8 -*-
import xml.sax, xml.sax.handler,re
# 管理类 XML
class XmlHandler(xml.sax.handler.ContentHandler):
# 遇到开始标签时调用的函数
def startElement(self,name,attributs):
global depth
# 一连串空格(缩进)
print " " * depth,
# 属性
precisions=""
for (attrib,valeur) in attributs.items():
precisions+="(%s,%s) " % (attrib,valeur)
# 显示标签名称及可能的属性
if precisions :
print "(%s,%s)" % (name,precisions)
else :
print name
# 增加一个层级
depth+=1
# 这是数据标签吗?
global balisesDonnees,baliseDeDonnees
if balisesDonnees.has_key(name.lower()):
baliseDeDonnees=1
# 遇到结束标签时调用的函数
def endElement(self,name):
# 标签结束
# 缩进级别
global depth
depth-=1
# 一串空格(缩进)
print " " * depth,
# 标签名称
print "/%s" % (name)
# 数据显示函数
def characters(self,data):
# 数据
global baliseDeDonnees
# 当前标签是否为数据标签?
if not baliseDeDonnees :
return
# 缩进级别
global depth
# 一连串空格(缩进)
print " " * depth,
# 显示数据
match=re.match(r"^\s*(.*)\s*$",data)
if match:
print "[%s]" % (match.groups()[0])
# 数据标签结束
baliseDeDonnees=False
# ------------------------------------------- main
# 程序
# 数据
file="data.xml" # XML文件
depth=0 # 缩进级别=树结构中的层级深度
balisesDonnees={"nom":1,"prenom":1,"age":1,"section":1,"formation":1}
baliseDeDonnees=True # 为真时,表示当前处理的是数据标签
# 创建一个 XML 文本解析对象
xml_parser=xml.sax.make_parser()
# 标签管理器
xml_parser.setContentHandler(XmlHandler())
# 解析 XML 文件
xml_parser.parse(file)
注:
- 该脚本使用了模块 xml.sax、xml.sax.handler 的函数库(第 3 行);
- 第 62 行:已解析的文件 XML;
- 第 68 行:解析器 XML;
- 第 70 行:解析器触发的事件处理程序将是一个 XmlHandler 类的实例;
- 第 72 行:启动对文档 XML 的解析;
- 第 6 行:实现方法 startElement、endElement、characters 的类。 该类继承自类 xml.sax.handler.ContentHandler,该类实现了解析器所使用的方法;
- 第 9 行:方法 startElement;
- 第 30 行:方法 endElement;
- 第 41 行:方法 characters.
结果如本段开头所示。
