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

Нам потрібно знати, як розпізнати:

  • початковий тег, наприклад <formation>;
  • тег закінчення, наприклад </enseignant>;
  • тег початку з атрибутами, наприклад <personne sexe="F">;
  • тіло тегу, наприклад martin у <nom>martin</nom>.

Програма для аналізу коду XML називається парсером XML. Два модулі надають нам функціонал для аналізу коду XML: xml.sax та xml.sax.handler.

Модуль [xml.sax] надає нам парсер XML за допомогою інструкції:

xml_parser=xml.sax.make_parser()

Цей парсер послідовно аналізує текст XML. Він викликає методи користувача при таких подіях:

  • метод startElement при початку тегу;
  • метод endElement при кінці тегу;
  • метод characters — при обробці тіла тегу.

Нам потрібно вказати парсеру клас, що реалізує ці методи:

xml_parser.setContentHandler(XmlHandler())

Ми передаємо методу setContentHandler парсера екземпляр класу, що реалізує методи startElement, endElement, characters. Використовуваний клас є похідним від класу xml.sax.handler.ContentHandler. Попередні методи викликаються з параметрами:

def startElement(self,name,attributs):
  • name — це ім’я тегу початку. attributs — це словник атрибутів тегу. Отже, для тегу <personne sexe="M"> ми отримаємо name="personne" та attributs={'sexe':'M'}
def endElement(self,name):
  • name — це назва тегу завершення. Отже, для тегу </etudiant> отримаємо name='etudiant'.
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

# клас управління XML
class XmlHandler(xml.sax.handler.ContentHandler):

    # функція, що викликається при виявленні тегу початку
    def startElement(self,name,attributs):
        global depth
        # послідовність пробілів (відступ)
        print " " * depth,
        # атрибути
        precisions=""
        for (attrib,valeur) in attributs.items():
            precisions+="(%s,%s) " % (attrib,valeur)
        # виводиться назва тегу та можливі атрибути
        if precisions :
            print "(%s,%s)" % (name,precisions)
        else :
            print name
        # додатковий рівень дерева
        depth+=1
        # чи це тег даних?
        global balisesDonnees,baliseDeDonnees
        if balisesDonnees.has_key(name.lower()):
            baliseDeDonnees=1

    # функція, що викликається при виявленні тегу закінчення
    def endElement(self,name):
        # кінець тегу
        # рівень відступу
        global depth
        depth-=1
        # послідовність пробілів (відступ)
        print " " * depth,
        # ім'я тегу
        print "/%s" % (name)

    # функція відображення даних
    def characters(self,data):
        # дані
        global baliseDeDonnees

        # чи є поточний тег тегом даних?
        if not baliseDeDonnees :
            return
        # рівень відступу
        global depth
        # послідовність пробілів (відступ)
        print " " * depth,
        # дані відображаються
        match=re.match(r"^\s*(.*)\s*$",data)
        if match:
            print "[%s]" % (match.groups()[0])
        # кінець тегу даних
        baliseDeDonnees=False

# ------------------------------------------- main  
# програма
# дані
file="data.xml"       # XML-файл
depth=0               # рівень втягнення = глибина в дереві
balisesDonnees={"nom":1,"prenom":1,"age":1,"section":1,"formation":1}
baliseDeDonnees=True     # якщо має значення «true», вказує, що це тег даних

# створюється об’єкт аналізу XML-тексту
xml_parser=xml.sax.make_parser()
# менеджер тегів
xml_parser.setContentHandler(XmlHandler())
# обробка XML-файлу
xml_parser.parse(file)

Примітки:

  • скрипт використовує бібліотеку функцій модулів xml.sax, xml.sax.handler (рядок 3);
  • рядок 62: проаналізований файл XML;
  • рядок 68: парсер XML;
  • рядок 70: обробник подій, що генеруються парсером, буде екземпляром класу XmlHandler;
  • рядок 72: розпочато аналіз документа XML;
  • рядок 6: клас, що реалізує методи startElement, endElement, characters. Він походить від класу xml.sax.handler.ContentHandler, який реалізує методи, що використовуються парсером;
  • рядок 9: метод startElement;
  • рядок 30: метод endElement;
  • рядок 41: метод characters.

Результати наведені на початку цього абзацу.