Skip to content

14. Procesamiento de documentos XML

  

Consideramos el siguiente 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>

Analizamos este documento para generar la siguiente salida de consola:

 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

Debemos saber cómo reconocer:

  • una etiqueta de inicio como <formación>;
  • una etiqueta de cierre como </enseñante>;
  • una etiqueta de inicio con atributos como <persona sexo="F">;
  • el cuerpo de una etiqueta como martin en <nombre>martin</nombre>.

El programa de análisis de un código XML se denomina analizador XML. Dos módulos nos proporcionan las funcionalidades para analizar un código XML: xml.sax y xml.sax.handler.

El módulo [xml.sax] nos proporciona un analizador XML mediante la instrucción:

xml_parser=xml.sax.make_parser()

Este analizador analiza el texto XML de forma secuencial. Llama a métodos del usuario en determinados eventos:

  • el método startElement al inicio de una etiqueta;
  • el método endElement al final de una etiqueta;
  • el método characters en el cuerpo de una etiqueta.

Debemos indicar al analizador la clase que implementa estos métodos:

xml_parser.setContentHandler(XmlHandler())

Pasamos al método setContentHandler del analizador una instancia de la clase que implementa los métodos startElement, endElement y characters. La clase utilizada es una clase derivada de la clase xml.sax.handler.ContentHandler. Los métodos anteriores se invocan con los siguientes parámetros:

def startElement(self,name,attributs):
  • name es el nombre de la etiqueta de inicio. attributes es el diccionario de atributos de la etiqueta. Así, para la etiqueta <personne sexe="M"> tendremos name="personne" y attributes={'sexe':'M'}
def endElement(self,name):
  • name es el nombre de la etiqueta de cierre. Así, para la etiqueta </estudiante> tendremos name='estudiante'.
def characters(self,data):
  • data es el cuerpo de la etiqueta. Por lo tanto, si la etiqueta es
<nom>
    dupont
</nom>

obtendremos data='\r\n dupont\r\n '. Por lo general, se eliminarán los espacios que preceden y siguen a los datos.

Una vez explicado esto, podemos pasar al script de análisis de un documento XML:


El programa (xml_sax_01)


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

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

# clase de gestión XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # función que se invoca al encontrar una etiqueta de inicio
    def startElement(self,name,attributs):
        global depth
        # una secuencia de espacios (sangría)
        print " " * depth,
        # atributos
        precisions=""
        for (attrib,valeur) in attributs.items():
            precisions+="(%s,%s) " % (attrib,valeur)
        # se muestra el nombre de la etiqueta y los posibles atributos
        if precisions :
            print "(%s,%s)" % (name,precisions)
        else :
            print name
        # un nivel más en el árbol
        depth+=1
        # ¿Es una etiqueta de datos?
        global balisesDonnees,baliseDeDonnees
        if balisesDonnees.has_key(name.lower()):
            baliseDeDonnees=1

    # la función que se invoca al encontrar una etiqueta de cierre
    def endElement(self,name):
        # fin de etiqueta
        # nivel de sangría
        global depth
        depth-=1
        # una secuencia de espacios (sangría)
        print " " * depth,
        # el nombre de la etiqueta
        print "/%s" % (name)

    # la función de visualización de datos
    def characters(self,data):
        # datos
        global baliseDeDonnees

        # ¿Es la etiqueta actual una etiqueta de datos?
        if not baliseDeDonnees :
            return
        # nivel de sangría
        global depth
        # una secuencia de espacios (sangría)
        print " " * depth,
        # se muestran los datos
        match=re.match(r"^\s*(.*)\s*$",data)
        if match:
            print "[%s]" % (match.groups()[0])
        # fin de la etiqueta de datos
        baliseDeDonnees=False

# ------------------------------------------- main  
# el programa
# datos
file="data.xml"       # el archivo xml
depth=0               # nivel de sangría = profundidad en el árbol
balisesDonnees={"nom":1,"prenom":1,"age":1,"section":1,"formation":1}
baliseDeDonnees=True     # si es verdadero, indica que se trata de una etiqueta de datos

# se crea un objeto de análisis de texto xml
xml_parser=xml.sax.make_parser()
# el gestor de etiquetas
xml_parser.setContentHandler(XmlHandler())
# explotación del archivo xml
xml_parser.parse(file)

Notas:

  • el script utiliza la biblioteca de funciones de los módulos xml.sax, xml.sax.handler (línea 3);
  • línea 62: el archivo XML analizado;
  • línea 68: el analizador XML;
  • línea 70: el gestor de eventos emitidos por el analizador será una instancia de la clase XmlHandler;
  • línea 72: se inicia el análisis del documento XML;
  • línea 6: la clase que implementa los métodos startElement, endElement, characters. Deriva de la clase xml.sax.handler.ContentHandler, que implementa los métodos utilizados por el analizador;
  • línea 9: el método startElement;
  • línea 30: el método endElement;
  • línea 41: el método characters.

Los resultados son los que se presentan al principio de este párrafo.