Skip to content

14. Elaborazione dei documenti XML

  

Consideriamo il seguente documento 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>

Analizziamo questo documento per ottenere il seguente output della console:

 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

Dobbiamo sapere come riconoscere:

  • un tag di apertura come <formazione>;
  • un tag di chiusura come </insegnante>;
  • un tag di apertura con attributi come <personne sexe="F">;
  • il corpo di un tag come martin in <nom>martin</nom>.

Il programma che analizza un codice XML è chiamato parser XML. Due moduli ci forniscono le funzionalità per analizzare un codice XML: xml.sax e xml.sax.handler.

Il modulo [xml.sax] ci fornisce un parser XML tramite l'istruzione:

xml_parser=xml.sax.make_parser()

Questo parser analizza il testo XML in modo sequenziale. Richiama i metodi dell’utente in occasione di determinati eventi:

  • il metodo startElement all'inizio di un tag;
  • il metodo endElement alla fine di un tag;
  • il metodo characters sul corpo di un tag.

Dobbiamo indicare al parser la classe che implementa questi metodi:

xml_parser.setContentHandler(XmlHandler())

Passiamo al metodo setContentHandler del parser, un'istanza della classe che implementa i metodi startElement, endElement, characters. La classe utilizzata è una classe derivata dalla classe xml.sax.handler.ContentHandler. I metodi precedenti vengono chiamati con i seguenti parametri:

def startElement(self,name,attributs):
  • name è il nome del tag di apertura. attributs è il dizionario degli attributi del tag. Pertanto, per il tag <personne sexe="M"> avremo name="personne" e attributs={'sexe':'M'}
def endElement(self,name):
  • name è il nome del tag di chiusura. Pertanto, per il tag </etudiant> si avrà name='etudiant'.
def characters(self,data):
  • data è il corpo del tag. Quindi, se il tag è
<nom>
    dupont
</nom>

si avrà data='\r\n dupont\r\n '. In generale, si eliminano gli spazi che precedono e seguono il dato.

Chiarito questo, si può passare allo script di analisi di un documento XML:


Il programma (xml_sax_01)


# -*- coding=utf-8 -*-

import xml.sax, xml.sax.handler,re

# classe di gestione XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # funzione chiamata all'incontro di un tag di inizio
    def startElement(self,name,attributs):
        global depth
        # una sequenza di spazi (indentazione)
        print " " * depth,
        # attributi
        precisions=""
        for (attrib,valeur) in attributs.items():
            precisions+="(%s,%s) " % (attrib,valeur)
        # vengono visualizzati il nome del tag e gli eventuali attributi
        if precisions :
            print "(%s,%s)" % (name,precisions)
        else :
            print name
        # un ulteriore livello della struttura ad albero
        depth+=1
        # si tratta di un tag di dati?
        global balisesDonnees,baliseDeDonnees
        if balisesDonnees.has_key(name.lower()):
            baliseDeDonnees=1

    # la funzione chiamata quando si incontra un tag di chiusura
    def endElement(self,name):
        # fine del tag
        # livello di indentazione
        global depth
        depth-=1
        # una sequenza di spazi (indentazione)
        print " " * depth,
        # il nome del tag
        print "/%s" % (name)

    # funzione di visualizzazione dei dati
    def characters(self,data):
        # dati
        global baliseDeDonnees

        # il tag corrente è un tag di dati?
        if not baliseDeDonnees :
            return
        # livello di indentazione
        global depth
        # una sequenza di spazi (indentazione)
        print " " * depth,
        # i dati vengono visualizzati
        match=re.match(r"^\s*(.*)\s*$",data)
        if match:
            print "[%s]" % (match.groups()[0])
        # fine del tag dati
        baliseDeDonnees=False

# ------------------------------------------- main  
# il programma
# dati
file="data.xml"       # il file xml
depth=0               # livello di indentazione = profondità nella struttura ad albero
balisesDonnees={"nom":1,"prenom":1,"age":1,"section":1,"formation":1}
baliseDeDonnees=True     # se vero, indica che si tratta di un tag di dati

# si crea un oggetto di analisi del testo XML
xml_parser=xml.sax.make_parser()
# il gestore dei tag
xml_parser.setContentHandler(XmlHandler())
# elaborazione del file XML
xml_parser.parse(file)

Note:

  • lo script utilizza la libreria di funzioni dei moduli xml.sax, xml.sax.handler (riga 3);
  • riga 62: il file XML analizzato;
  • riga 68: il parser XML;
  • riga 70: il gestore degli eventi generati dal parser sarà un'istanza della classe XmlHandler;
  • riga 72: viene avviata l'analisi del documento XML;
  • riga 6: la classe che implementa i metodi startElement, endElement, characters. Essa deriva dalla classe xml.sax.handler.ContentHandler che implementa i metodi utilizzati dal parser;
  • riga 9: il metodo startElement;
  • riga 30: il metodo endElement;
  • riga 41: il metodo characters.

I risultati sono quelli presentati all'inizio di questo paragrafo.