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
الآن بعد الانتهاء من ذلك، دعونا نلقي نظرة على مثال لما يمكننا فعله باستخدام هذه الوحدة النمطية:

النص البرمجي [xml_01] هو كما يلي:
- الأسطر 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]:
- السطر 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]؛