Skip to content

14. Processamento de documentos XML

  

Consideremos o seguinte 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>

Analisamos este documento para produzir a seguinte saída na 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

Temos de saber como reconhecer:

  • uma baliza de início, como <formação>;
  • uma baliza de fim, como </enseignant>;
  • uma baliza de início com atributos, como <pessoa sexo="F">;
  • o corpo de uma baliza como martin em <nom>martin</nom>.

O programa de análise de um código XML é denominado analisador XML. Dois módulos fornecem-nos as funcionalidades para analisar um código XML: xml.sax e xml.sax.handler.

O módulo [xml.sax] fornece-nos um analisador XML através da instrução:

xml_parser=xml.sax.make_parser()

Este analisador analisa o texto XML sequencialmente. Chama métodos do utilizador em eventos:

  • o método startElement ao início de uma baliza;
  • o método endElement ao encontrar o fim de uma baliza;
  • o método characters no corpo de uma baliza.

Temos de indicar ao analisador a classe que implementa estes métodos:

xml_parser.setContentHandler(XmlHandler())

Passamos ao método setContentHandler do analisador uma instância da classe que implementa os métodos startElement, endElement e characters. A classe utilizada é uma classe derivada da classe xml.sax.handler.ContentHandler. Os métodos anteriores são chamados com os seguintes parâmetros:

def startElement(self,name,attributs):
  • name é o nome da baliza de início. attributs é o dicionário de atributos da baliza. Assim, para a baliza <pessoa sexo="M">, teremos name="personne" e attributs={'sexe':'M'}
def endElement(self,name):
  • name é o nome da baliza de fim. Assim, para a baliza </etudiant>, teremos name='etudiant'.
def characters(self,data):
  • data é o corpo da baliza. Assim, se a baliza for
<nom>
    dupont
</nom>

teremos data='\r\n dupont\r\n '. Em geral, eliminam-se os espaços que precedem e seguem os dados.

Dito isto, podemos passar ao script de análise de um documento XML:


O programa (xml_sax_01)


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

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

# classe de gestão XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # função chamada ao encontrar uma baliza de início
    def startElement(self,name,attributs):
        global depth
        # uma sequência de espaços (indentação)
        print " " * depth,
        # atributos
        precisions=""
        for (attrib,valeur) in attributs.items():
            precisions+="(%s,%s) " % (attrib,valeur)
        # exibe-se o nome da baliza e os eventuais atributos
        if precisions :
            print "(%s,%s)" % (name,precisions)
        else :
            print name
        # mais um nível na árvore
        depth+=1
        # trata-se de uma baliza de dados?
        global balisesDonnees,baliseDeDonnees
        if balisesDonnees.has_key(name.lower()):
            baliseDeDonnees=1

    # a função chamada quando se encontra uma baliza de fim
    def endElement(self,name):
        # fim da baliza
        # nível de indentação
        global depth
        depth-=1
        # uma sequência de espaços (indentação)
        print " " * depth,
        # o nome da baliza
        print "/%s" % (name)

    # a função de exibição dos dados
    def characters(self,data):
        # dados
        global baliseDeDonnees

        # a tag atual é uma tag de dados?
        if not baliseDeDonnees :
            return
        # nível de indentação
        global depth
        # uma sequência de espaços (indentação)
        print " " * depth,
        # os dados são apresentados
        match=re.match(r"^\s*(.*)\s*$",data)
        if match:
            print "[%s]" % (match.groups()[0])
        # fim da baliza de dados
        baliseDeDonnees=False

# ------------------------------------------- main  
# o programa
# dados
file="data.xml"       # o ficheiro XML
depth=0               # nível de indentação = profundidade na árvore
balisesDonnees={"nom":1,"prenom":1,"age":1,"section":1,"formation":1}
baliseDeDonnees=True     # se for verdadeiro, indica que se trata de uma baliza de dados

# cria-se um objeto de análise de texto XML
xml_parser=xml.sax.make_parser()
# o gestor de tags
xml_parser.setContentHandler(XmlHandler())
# análise do ficheiro XML
xml_parser.parse(file)

Notas:

  • o script utiliza a biblioteca de funções dos módulos xml.sax e xml.sax.handler (linha 3);
  • linha 62: o ficheiro XML analisado;
  • linha 68: o analisador XML;
  • linha 70: o gestor de eventos emitidos pelo analisador será uma instância da classe XmlHandler;
  • linha 72: é iniciada a análise do documento XML;
  • linha 6: a classe que implementa os métodos startElement, endElement, characters. Esta é derivada da classe xml.sax.handler.ContentHandler, que implementa métodos utilizados pelo analisador;
  • linha 9: o método startElement;
  • linha 30: o método endElement;
  • linha 41: o método characters.

Os resultados são os apresentados no início deste parágrafo.