Skip to content

14. Processing of document XML

  

Consider the following XML document:

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

We parse this document to produce the following console output:

 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

We need to know how to recognize:

  • a start tag such as <training>;
  • an end tag such as </teacher>;
  • a start tag with attributes such as <person gender="F">;
  • the body of a tag such as martin in <name>martin</name>.

The program that analyzes a XML code is called a XML parser. Two modules provide the functionality to parse a XML code: xml.sax and xml.sax.handler.

The [xml.sax] module provides us with a XML parser using the following statement:

xml_parser=xml.sax.make_parser()

This parser parses the text XML sequentially. It calls user methods on events:

  • the startElement method on a tag start;
  • the endElement method on a tag end;
  • the characters method on the body of a tag.

We need to tell the parser which class implements these methods:

xml_parser.setContentHandler(XmlHandler())

We pass to the parser’s setContentHandler method an instance of a class that implements the startElement, endElement, and characters methods. The class used is a subclass of the xml.sax.handler.ContentHandler class. The preceding methods are called with parameters:

def startElement(self,name,attributs):
  • name is the name of the start tag. attributes is the dictionary of the tag's attributes. Thus, for the tag <person sex="M">, we will have name="person" and attributes={'sex':'M'}
def endElement(self,name):
  • name is the name of the closing tag. Thus, for the </student> tag, we will have name='student'.
def characters(self,data):
  • data is the body of the tag. Thus, if the tag is
<nom>
    dupont
</nom>

we will have data='\r\n dupont\r\n '. Generally, we will remove the whitespace preceding and following the data.

With that explained, we can move on to the script for parsing a XML document:


The program (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 a text analysis object xml
xml_parser=xml.sax.make_parser()
# the tag manager
xml_parser.setContentHandler(XmlHandler())
# use of the xml file
xml_parser.parse(file)

Notes:

  • The script uses the function library from the modules xml.sax and xml.sax.handler (line 3);
  • line 62: the parsed XML file;
  • line 68: the XML parser;
  • line 70: the handler for events emitted by the parser will be an instance of the XmlHandler class;
  • line 72: the parsing of the XML document is initiated;
  • line 6: the class implementing the methods startElement, endElement, and characters. It is derived from the class xml.sax.handler.ContentHandler, which implements methods used by the parser;
  • line 9: the method startElement;
  • line 30: the endElement method;
  • line 41: the characters method.

The results are those presented at the beginning of this paragraph.