Skip to content

26. من القاموس إلى XML والعكس

هنا، سنستكشف الوحدة النمطية [xml2dict]، التي تسمح لك بالتحويل:

  • سلسلة XML إلى قاموس:
  • قاموس إلى سلسلة XML؛

قبل ظهور JSON، كانت استجابات خدمات الويب غالبًا بتنسيق XML (لغة الترميز الموسعة). علاوة على ذلك، كان بروتوكول خدمات الويب هذه غالبًا هو SOAP (بروتوكول الوصول البسيط إلى الكائنات). SOAP هو بروتوكول يعتمد على بروتوكول HTTP للويب. حاليًا (2020)، تكون خدمات الويب في الغالب من نوع REST (نقل الحالة التمثيلية). خدمات الويب التي درسناها ليست من أي من هذين النوعين، ولكنها بالتأكيد أقرب إلى REST منها إلى SOAP. ومع ذلك، أفضل القول إنها من النوع "الحر" أو "المجهول" لأنها لا تتبع جميع قواعد REST.

سنوضح مدى سهولة تحويل بنى عميل/خادم JSON الخاصة بنا إلى بنى عميل/خادم XML. كل ما عليك فعله هو استخدام الوحدة النمطية [xmltodict].

سنبدأ بتثبيتها في محطة Python:


(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install xmltodict
Collecting xmltodict
  Using cached xmltodict-0.12.0-py2.py3-none-any.whl (9.2 kB)
Installing collected packages: xmltodict
Successfully installed xmltodict-0.12.0

الآن بعد الانتهاء من ذلك، دعونا نلقي نظرة على مثال لما يمكننا فعله باستخدام هذه الوحدة النمطية:

Image

النص البرمجي [xml_01] هو كما يلي:

from collections import OrderedDict

import xmltodict


#  xmltodict.parse to pass from XML to the dictionary. The dictionary must have a root
#  the dictionary produced is of type OrderedDict
#  xmltodict.unparse to go from the dictionary to XML

def ordereddict2dict(ordered_dictionary) -> dict:
    


def transform(message: str, dictionary: dict):
    #  logs
    print(f"\n{message}-------")
    print(f"dictionnaire={dictionary}")
    #  dict -> xml
    xml1 = xmltodict.unparse(dictionary)
    print(f"xml={xml1}")
    #  xml -> OrderedDict
    ordereddict_dictionary1 = xmltodict.parse(xml1)
    print(f"ordereddict_dictionary1={ordereddict_dictionary1}")
    #  OrderedDict -> dict
    print(f"dict_dictionary1={ordereddict2dict(ordereddict_dictionary1)}")


#  test 1
transform("test 1", {"nom": "séléné"})
#  test 2
transform("test 2", {"famille": {"père": {"prénom": "andré"}, "mère": {"prénom": "angèle"}, "nom": "séléné"}})
#  test 3
transform("test 3", {"famille": {"nom": "séléné", "père": {"prénom": "andré"}, "mère": {"prénom": "angèle"},
                                 "hobbies": ["chant", "footing"]}})
#  test 4
transform("test 4", {'réponse': {
    'erreurs': ['Méthode GET requise avec les seuls paramètres [marié, enfants, salaire]', 'paramètre [marié] manquant',
                'paramètre [enfants] manquant', 'paramètre [salaire] manquant']}})
#  test 5
transform("test 5", {'réponse': {
    'result': {'id': 0, 'marié': 'oui', 'enfants': 2, 'salaire': 50000, 'impôt': 1384, 'décôte': 384, 'surcôte': 0,
               'réduction': 347, 'taux': 0.14}}})
#  test 6
transform("test 6", {"root": {'liste': ["un", "deux", "trois"]}})
#  test 7
transform("test 7", {"root": {'liste': [{"un": [10, 11]}, {"deux": [20, 21]}, {"trois": [30, 31]}]}})
  • الأسطر 14–25: تأخذ الدالة [transform] نصًا ليتم كتابته [message] وقاموسًا [dictionary]؛
  • السطر 16: يعرض الرسالة؛
  • السطر 17: يتم عرض القاموس المستلم؛
  • السطور 19–20: يتم تحويل هذا القاموس إلى سلسلة XML، ثم يتم عرضها. الطريقة التي تقوم بهذه العملية هي [xmltodict.unparse]؛
  • الأسطر 21–23: يتم تحويل سلسلة XML السابقة إلى قاموس، ثم يتم عرضها. الطريقة التي تؤدي هذه المهمة هي [xmltodict.parse]. لا تنتج هذه الطريقة قاموسًا من النوع [dict] بل من النوع [OrderedDict] (السطر 1)؛
  • السطور 24-25: يتم تحويل النوع [OrderedDict] الناتج إلى [dict] باستخدام الطريقة (التي لم تُكتب بعد) [ordereddict2dict]. تعمل هذه الطريقة بشكل متكرر. إذا كانت قيم معينة في القاموس من النوع [OrderedDict, list]، يتم فحص قيم هذه المجموعات لتحديد ما إذا كانت هي أيضًا من النوع [OrderedDict]. إذا كان الأمر كذلك، يتم تحويلها إلى النوع [dict]. لاحظ أن الطريقة [xmltodict.parse] لا تنتج أي قواميس من النوع [dict]؛

قبل فحص الوظائف المفقودة، دعونا نلقي نظرة على النتائج لنرى ما الذي نبحث عنه:

يُنتج الاختبار 1 (السطران 28–29) النتائج التالية:


test 1-------
dictionnaire={'nom': 'séléné'}
xml=<?xml version="1.0" encoding="utf-8"?>
<nom>séléné</nom>
ordereddict_dictionary1=OrderedDict([('nom', 'séléné')])
dict_dictionary1={'nom': 'séléné'}
  • السطر 2: القاموس قيد الاختبار. لاحظ نقطة مهمة: تتطلب طريقة [xml2dict.unparse] أن يكون القاموس في صيغة {‘key’: value}، حيث يمكن أن يكون [value] قاموسًا أو قائمة أو نوعًا بسيطًا؛
  • السطران 3-4: سلسلة XML التي تم إنشاؤها من القاموس. يسبقها الرأس [<?xml version="1.0" encoding="utf-8"?>\n]، والذي عادةً ما يكون السطر الأول في ملف XML؛
  • السطر 5: النوع [OrderedDict] الذي تم الحصول عليه بواسطة الدالة [xml2dict.parse]، والتي تأخذ سلسلة XML السابقة كمعلمة؛
  • السطر 6: القاموس من النوع [dict] الذي تم الحصول عليه بتطبيق الأسلوب [ordereddict2dict] على النوع السابق. هذا هو القاموس الأصلي من السطر 2؛

تتبع جميع الاختبارات الأخرى نفس النمط وينبغي أن تساعدك على فهم كيفية التحويل من قاموس إلى سلسلة XML ثم من تلك السلسلة XML مرة أخرى إلى القاموس الأصلي.

تسفر الاختبارات الأخرى عن النتائج التالية:


test 2-------
dictionnaire={'famille': {'père': {'prénom': 'andré'}, 'mère': {'prénom': 'angèle'}, 'nom': 'séléné'}}
xml=<?xml version="1.0" encoding="utf-8"?>
<famille><père><prénom>andré</prénom></père><mère><prénom>angèle</prénom></mère><nom>séléné</nom></famille>
ordereddict_dictionary1=OrderedDict([('famille', OrderedDict([('père', OrderedDict([('prénom', 'andré')])), ('mère', OrderedDict([('prénom', 'angèle')])), ('nom', 'séléné')]))])
dict_dictionary1={'famille': {'père': {'prénom': 'andré'}, 'mère': {'prénom': 'angèle'}, 'nom': 'séléné'}}
 
test 3-------
dictionnaire={'famille': {'nom': 'séléné', 'père': {'prénom': 'andré'}, 'mère': {'prénom': 'angèle'}, 'hobbies': ['chant', 'footing']}}
xml=<?xml version="1.0" encoding="utf-8"?>
<famille><nom>séléné</nom><père><prénom>andré</prénom></père><mère><prénom>angèle</prénom></mère><hobbies>chant</hobbies><hobbies>footing</hobbies></famille>
ordereddict_dictionary1=OrderedDict([('famille', OrderedDict([('nom', 'séléné'), ('père', OrderedDict([('prénom', 'andré')])), ('mère', OrderedDict([('prénom', 'angèle')])), ('hobbies', ['chant', 'footing'])]))])
dict_dictionary1={'famille': {'nom': 'séléné', 'père': {'prénom': 'andré'}, 'mère': {'prénom': 'angèle'}, 'hobbies': ['chant', 'footing']}}
 
test 4-------
dictionnaire={'réponse': {'erreurs': ['Méthode GET requise avec les seuls paramètres [marié, enfants, salaire]', 'paramètre [marié] manquant', 'paramètre [enfants] manquant', 'paramètre [salaire] manquant']}}
xml=<?xml version="1.0" encoding="utf-8"?>
<réponse><erreurs>Méthode GET requise avec les seuls paramètres [marié, enfants, salaire]</erreurs><erreurs>paramètre [marié] manquant</erreurs><erreurs>paramètre [enfants] manquant</erreurs><erreurs>paramètre [salaire] manquant</erreurs></réponse>
ordereddict_dictionary1=OrderedDict([('réponse', OrderedDict([('erreurs', ['Méthode GET requise avec les seuls paramètres [marié, enfants, salaire]', 'paramètre [marié] manquant', 'paramètre [enfants] manquant', 'paramètre [salaire] manquant'])]))])
dict_dictionary1={'réponse': {'erreurs': ['Méthode GET requise avec les seuls paramètres [marié, enfants, salaire]', 'paramètre [marié] manquant', 'paramètre [enfants] manquant', 'paramètre [salaire] manquant']}}
 
test 5-------
dictionnaire={'réponse': {'result': {'id': 0, 'marié': 'oui', 'enfants': 2, 'salaire': 50000, 'impôt': 1384, 'décôte': 384, 'surcôte': 0, 'réduction': 347, 'taux': 0.14}}}
xml=<?xml version="1.0" encoding="utf-8"?>
<réponse><result><id>0</id><marié>oui</marié><enfants>2</enfants><salaire>50000</salaire><impôt>1384</impôt><décôte>384</décôte><surcôte>0</surcôte><réduction>347</réduction><taux>0.14</taux></result></réponse>
ordereddict_dictionary1=OrderedDict([('réponse', OrderedDict([('result', OrderedDict([('id', '0'), ('marié', 'oui'), ('enfants', '2'), ('salaire', '50000'), ('impôt', '1384'), ('décôte', '384'), ('surcôte', '0'), ('réduction', '347'), ('taux', '0.14')]))]))])
dict_dictionary1={'réponse': {'result': {'id': '0', 'marié': 'oui', 'enfants': '2', 'salaire': '50000', 'impôt': '1384', 'décôte': '384', 'surcôte': '0', 'réduction': '347', 'taux': '0.14'}}}
 
test 6-------
dictionnaire={'root': {'liste': ['un', 'deux', 'trois']}}
xml=<?xml version="1.0" encoding="utf-8"?>
<root><liste>un</liste><liste>deux</liste><liste>trois</liste></root>
ordereddict_dictionary1=OrderedDict([('root', OrderedDict([('liste', ['un', 'deux', 'trois'])]))])
dict_dictionary1={'root': {'liste': ['un', 'deux', 'trois']}}
 
test 7-------
dictionnaire={'root': {'liste': [{'un': [10, 11]}, {'deux': [20, 21]}, {'trois': [30, 31]}]}}
xml=<?xml version="1.0" encoding="utf-8"?>
<root><liste><un>10</un><un>11</un></liste><liste><deux>20</deux><deux>21</deux></liste><liste><trois>30</trois><trois>31</trois></liste></root>
ordereddict_dictionary1=OrderedDict([('root', OrderedDict([('liste', [OrderedDict([('un', ['10', '11'])]), OrderedDict([('deux', ['20', '21'])]), OrderedDict([('trois', ['30', '31'])])])]))])
dict_dictionary1={'root': {'liste': [{'un': ['10', '11']}, {'deux': ['20', '21']}, {'trois': ['30', '31']}]}}
 
Process finished with exit code 0
  • تسلط السطران 23 و27 الضوء على نقطة مهمة:
    • السطر 23: القيم المرتبطة بمفاتيح قاموس [result] هي أرقام؛
    • السطر 26: القيم المرتبطة بمفاتيح قاموس [ordereddict_dictionary1] هي سلاسل نصية. هذا هو أحد قيود مكتبة [xmltodict]. لا تنتج طريقة [parse] الخاصة بها سوى سلاسل نصية. وهذا أمر يسهل فهمه:
      • السطر 25: سلسلة XML التي تم إنشاء القاموس منها. في هذه السلسلة، لا توجد إشارة إلى نوع البيانات المغلفة داخل علامات XML. تقوم [xmltodict.parse] بما هو منطقي: تترك كل شيء كسلاسل في القاموس الناتج. هناك مكتبات أخرى مشابهة لـ [xmltodict] حيث يتم تحديد نوع البيانات المُغلفة في العلامات. على سبيل المثال، قد يجد المرء العلامة [<children type='int'>2</children>]؛
      • ونتيجة لذلك، عند استخدام قاموس تم إنشاؤه بواسطة وحدة [xmltodict]، يجب معرفة نوع البيانات التي يحتوي عليها من أجل التحويل من النوع ‘str’ إلى نوع البيانات الفعلي؛

دعونا الآن نلقي نظرة فاحصة على طريقة [ordereddict2dict]، التي تحول نوع [OrderedDict] إلى نوع [dict]:

#  xmltodict.parse to pass from XML to the dictionary. The dictionary must have a root
#  the dictionary produced is of type OrderedDict
#  xmltodict.unparse to go from the dictionary to XML

def check(value):
    #  if the value is of type OrderedDict, we transform it
    if isinstance(value, OrderedDict):
        value2 = ordereddict2dict(value)
    #  if the value is of type list, we transform it
    elif isinstance(value, list):
        value2 = list2list(value)
    else:
        #  we're dealing with a simple type, not a collection
        value2 = value
    #  we return the new value
    return value2


def list2list(liste: list) -> list:
    #  the new list
    newliste = []
    #  the elements of the parameter list are used
    for value in liste:
        #  add value to the new list
        newliste.append(check(value))
    #  return the new list
    return newliste


def ordereddict2dict(ordered_dictionary: OrderedDict) -> dict:
    #  OrderedDict -> recursive dictation
    newdict = {}
    for key, value in ordered_dictionary.items():
        #  store the value in the new dictionary
        newdict[key] = check(value)
    #  we return the dictionary
    return newdict
  • السطر 30: تأخذ الدالة [ordereddict2dict] نوع [OrderedDict] كمعلمة؛
  • السطر 32: القاموس من النوع [dict] الذي سيتم إرجاعه في السطر 37 بواسطة الدالة؛
  • السطر 33: نقوم بالتكرار على جميع المجموعات (key, value) في القاموس [ordered_dictionary]؛
  • السطر 35: في القاموس الجديد، يتم الاحتفاظ بالمفتاح [key]، لكن القيمة المرتبطة به ليست [value] بل [check(value)]. الدالة [check(value)] مسؤولة عن العثور، إذا كانت [value] مجموعة، على جميع العناصر من النوع [OrderedDict] وتحويلها إلى النوع [dict]؛

يتم تعريف طريقة [check] في الأسطر 5–16:

  • السطر 5: لا نعرف نوع [value]، لذا لم نتمكن من كتابة [value: type]؛
  • الأسطر 7-8: إذا كان [value] من النوع [OrderedDictفإننا نستدعي بشكل متكرر الدالة [ordereddict2dict] التي قمنا للتو بتعليقها؛
  • الأسطر 9–11: هناك حالة أخرى محتملة وهي أن [value] عبارة عن قائمة. في هذه الحالة، في السطر 11، نستدعي الدالة [list2list] من الأسطر 19–27؛
  • الأسطر 12–14: الحالة الأخيرة هي أن [value] ليست مجموعة بل نوع بسيط. الدالة [check]، مثل الدالتين [ordereddict2dict] و [list2listهي دالة تكرارية. نحن نعلم أنه في هذه الحالة، يجب علينا دائمًا التعامل مع الموقف الذي تنتهي فيه التكرارية. الأسطر 12–14 تتعامل مع هذه الحالة؛
  • السطر 16: تنتج الدالة [check]، سواء تم استدعاؤها بشكل متكرر أم لا، قيمة [value2] التي يجب أن تحل محل المعلمة [value] في السطر 5؛

تقوم الطريقة [list2list] المحددة في الأسطر 19–27 بمعالجة قائمة تم تمريرها كمعلمة. ستقوم بتفحصها واستبدال أي قيم [OrderedDict] موجودة بداخلها بنوع [dict].

  • السطر 21: القائمة الجديدة التي ستنشئها الدالة؛
  • الأسطر 23-25: يتم استعراض جميع قيم [value] في القائمة واستبدالها بالقيمة [check(value)]. قد تحتوي هذه [value] نفسها على عناصر من النوع [list] أو [OrderedDict]. وسيتم التعامل معها بشكل صحيح بواسطة الدالة التكرارية [check]؛