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

يجب أن نعرف كيفية التعرف على:

  • علامة البداية مثل <تدريب>؛
  • علامة نهاية مثل </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     # إذا كانت القيمة «صحيح»، فهذا يشير إلى أننا نتعامل مع علامة بيانات

# يتم إنشاء كائن لتحليل نص 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.

النتائج هي تلك المعروضة في بداية هذه الفقرة.