Skip to content

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.saxxml.sax.handler

[xml.sax] 模块通过以下语句为我们提供了一个 XML 解析器:

xml_parser=xml.sax.make_parser()

该解析器按顺序分析文本 XML。它会在以下事件发生时调用用户的方法:

  • 在标签开始时调用方法 startElement
  • 在标签结束时调用方法 endElement
  • 在标签主体处调用方法 characters

我们需要向解析器指定实现这些方法的类:

xml_parser.setContentHandler(XmlHandler())

我们将实现方法 startElementendElementcharacters 的类实例传递给解析器的 setContentHandler 方法。 所使用的类是 xml.sax.handler.ContentHandler 类的派生类。上述方法通过以下参数被调用:

def startElement(self,name,attributs):
  • name 是起始标签的名称。attributs 是该标签的属性字典。 因此,对于标签 <personne sexe="M">,将得到 name="personne"attributs={'sexe':'M'}
def endElement(self,name):
  • name 是结束标签的名称。因此,对于标签 </etudiant>,对应的结束标签为 name='etudiant'
def characters(self,data):
  • data 是标签的正文。因此,如果标签是
<nom>
    dupont
</nom>

我们将得到 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.saxxml.sax.handler 的函数库(第 3 行);
  • 第 62 行:已解析的文件 XML;
  • 第 68 行:解析器 XML;
  • 第 70 行:解析器触发的事件处理程序将是一个 XmlHandler 类的实例
  • 第 72 行:启动对文档 XML 的解析;
  • 第 6 行:实现方法 startElementendElementcharacters 的类。 该类继承自类 xml.sax.handler.ContentHandler,该类实现了解析器所使用的方法;
  • 第 9 行:方法 startElement;
  • 第 30 行:方法 endElement;
  • 第 41 行:方法 characters.

结果如本段开头所示。