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

ما باید بدانیم چگونه تشخیص دهیم:

  • یک تگ آغازین مانند <training>;
  • یک تگ پایانی مانند </teacher
  • یک تگ بازشونده با ویژگی‌ها، مانند <person sex="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())

ما یک نمونه از کلاسی را که متدهای startElement، endElement و characters را پیاده‌سازی می‌کند، به متد setContentHandler پارسر پاس می‌کنیم. کلاس مورد استفاده زیرکلاس کلاس xml.sax.handler.ContentHandler است. متدهای قبلی با پارامترهای زیر فراخوانی می‌شوند:

def startElement(self,name,attributs):
  • name نام تگ آغازین است. attributs فرهنگ لغت ویژگی‌های تگ است. بنابراین، برای تگ <person sex="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

# ------------------------------------------- اصلی  
# برنامه
# داده‌ها
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 (خط ۳) استفاده می‌کند؛
  • خط ۶۲: فایل تحلیل‌شده XML;
  • خط ۶۸: پارسر XML؛
  • خط ۷۰: رسیدگی‌کننده برای رویدادهای صادرشده توسط تجزیه‌کننده، یک نمونه از کلاس XmlHandler خواهد بود؛
  • خط ۷۲: پردازش سند XML آغاز می‌شود؛
  • خط ۶: کلاسی که متدهای startElement، endElement و characters را پیاده‌سازی می‌کند. این از کلاس xml.sax.handler.ContentHandler مشتق شده است، که متدهای مورد استفاده توسط تجزیه‌گر را پیاده‌سازی می‌کند؛
  • خط ۹: متد startElement;
  • خط ۳۰: متد endElement;
  • خط ۴۱: متد characters.

نتایج همان‌هایی هستند که در ابتدای این پاراگراف ارائه شده‌اند.