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

我们需要知道如何识别:

  • 起始标签,例如 <training>;
  • 结束标签,例如 </teacher>;
  • 带有属性的起始标签,例如 <person gender="F">;
  • 标签的正文,例如 <name>martin</name> 中的 "martin"。

解析 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 是起始标签的名称。attributes 是该标签属性的字典。因此,对于标签 <person sex="M">,我们将有 name="person"attributes={'sex':'M'}
def endElement(self,name):
  • name 是结束标签的名称。因此,对于 </student> 标签,我们将得到 name='student'
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

#    management class XML
class XmlHandler(xml.sax.handler.ContentHandler):

    #  function called when a start tag is encountered
    def startElement(self,name,attributs):
        global depth
        #    a sequence of spaces (indentation)
        print " " * depth,
        #    attributes
        precisions=""
        for (attrib,valeur) in attributs.items():
            precisions+="(%s,%s) " % (attrib,valeur)
        #  displays the tag name and any attributes
        if precisions :
            print "(%s,%s)" % (name,precisions)
        else :
            print name
        #    an extra level of tree structure
        depth+=1
        #  is it a data tag?
        global balisesDonnees,baliseDeDonnees
        if balisesDonnees.has_key(name.lower()):
            baliseDeDonnees=1

    #  the function called when an end tag is encountered
    def endElement(self,name):
        #    end of tag
        #    indentation level
        global depth
        depth-=1
        #    a sequence of spaces (indentation)
        print " " * depth,
        #    tag name
        print "/%s" % (name)

    #    data display function
    def characters(self,data):
        #    data
        global baliseDeDonnees

        #  is the current tag a data tag?
        if not baliseDeDonnees :
            return
        #    indentation level
        global depth
        #    a sequence of spaces (indentation)
        print " " * depth,
        #  data are displayed
        match=re.match(r"^\s*(.*)\s*$",data)
        if match:
            print "[%s]" % (match.groups()[0])
        #    end of data tag
        baliseDeDonnees=False

#    ------------------------------------------- main  
#  the program
#    data
file="data.xml"       #  the xml file
depth=0               #  indentation level=depth in tree structure
balisesDonnees={"nom":1,"prenom":1,"age":1,"section":1,"formation":1}
baliseDeDonnees=True     #    to true, indicates a data tag

#    create an xml text analysis object
xml_parser=xml.sax.make_parser()
#  the tag manager
xml_parser.setContentHandler(XmlHandler())
#    xml file processing
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 方法

结果即本段开头所示。