26. Sözlükten XML'e ve tersi
Burada, aşağıdakileri dönüştürmeye olanak tanıyan [xml2dict] modülünü inceleyeceğiz:
- bir XML dizesini sözlüğe:
- bir sözlüğü XML dizesine dönüştürmek;
jSON ortaya çıkmadan önce, web hizmetlerinin yanıtı genellikle XML (eXtended İşaretleme Dili) formatındaydı. Ayrıca, bu web hizmetlerinin protokolü genellikle SOAP (Simple Object Process Protocol) idi. SOAP, web'in HTTP protokolüne dayanan bir protokoldür. Şu anda (2020), web hizmetleri daha çok REST (Representational State Transfer) türündedir. İncelediğimiz web hizmetleri bu türlerden hiçbirine uymamaktadır, ancak SOAP’ten çok REST’e daha yakındır. Yine de, REST'in tüm kurallarına uymadıkları için bunların "serbest" veya "bilinmeyen" türde olduğunu söylemeyi tercih ederim.
jSON istemci/sunucu mimarilerimizi XML istemci/sunucu mimarilerine dönüştürmenin ne kadar kolay olduğunu göstereceğiz. Bunun için [xmltodict] modülünü kullanmak yeterlidir.
Öncelikle bunu bir Python terminaline kuruyoruz:
(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
Bunu yaptıktan sonra, bu modülle neler yapabileceğimizi bir örnek üzerinden inceleyeceğiz:

[xml_01] komut dosyası şu şekildedir:
from collections import OrderedDict
import xmltodict
# xmltodict.parse, XML'ten sözlüğe geçiş yapmak için kullanılır. Sözlüğün bir kökü olmalıdır
# oluşturulan sözlük, OrderedDict türündedir
# xmltodict.unparse, sözlükten XML'e geçmek için
def ordereddict2dict(ordered_dictionary) -> dict:
…
def transform(message: str, dictionary: dict):
# günlükler
print(f"\n{message}-------")
print(f"dictionnaire={dictionary}")
# sözlük -> xml
xml1 = xmltodict.unparse(dictionary)
print(f"xml={xml1}")
# xml -> OrderedDict
ordereddict_dictionary1 = xmltodict.parse(xml1)
print(f"ordereddict_dictionary1={ordereddict_dictionary1}")
# OrderedDict -> sözlük
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': {
'hatalar': ['GET yöntemi yalnızca [marié, enfants, salaire] parametreleriyle kullanılmalıdır', '[marié] parametresi eksik',
'[enfants] parametresi eksik', '[salaire] parametresi eksik']}})
# test 5
transform("test 5", {'réponse': {
'sonuç': {'id': 0, 'evli': 'evet', 'çocuk': 2, 'maaş': 50000, 'vergi': 1384, 'indirim': 384, 'ek vergi': 0,
'indirim': 347, 'oran': 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. satırlar: [transform] işlevi, yazılacak bir metin [message] ve bir sözlük [dictionary] alır;
- 16. satır: mesajın görüntülenmesi;
- 17. satır: Alınan sözlük görüntülenir;
- 19-20. satırlar: Bu sözlük, XML dizesine dönüştürülür ve bu dizi görüntülenir. Bunu gerçekleştiren yöntem [xmltodict.unparse]'tir;
- 21-23. satırlar: önceki XML dizesi sözlüğe dönüştürülür ve bu sözlük görüntülenir. Bunu gerçekleştiren yöntem [xmltodict.parse]'tir. Bu yöntem, [dict] türünde değil, [OrderedDict] türünde bir sözlük üretir (1. satır);
- 24-25. satırlar: Elde edilen [OrderedDict] türü, (henüz yazılmamış) [ordereddict2dict] yöntemi kullanılarak [dict] türüne dönüştürülür. Bu yöntem özyinelemeli olarak çalışır. Sözlüğün bazı değerleri [OrderedDict, list] türündeyse, bu koleksiyonların değerleri de [OrderedDict] türünde olup olmadıkları kontrol edilir. Eğer öyleyse, bunlar [dict] türüne dönüştürülür. [xmltodict.parse] yönteminin [dict] türünde hiçbir sözlük üretmediğine dikkat edilmelidir;
Eksik fonksiyonları incelemeden önce, ne arandığını görmek için sonuçlara bir göz atalım:
Test 1 (28-29. satırlar) şu sonuçları verir:
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. satır: test edilen sözlük. Önemli bir noktaya dikkat etmek gerekir: [xml2dict.unparse] yöntemi, sözlüğün {‘anahtar’: değer} biçiminde olmasını gerektirir; burada [valeur] daha sonra bir sözlük, bir liste veya basit bir tür olabilir;
- 3-4. satırlar: sözlükten elde edilen XML dizesi. Bu dizeden önce, normalde bir XML dosyasının ilk satırı olan [<?xml version="1.0" encoding="utf-8"?>\n] başlığı gelir;
- 5. satır: önceki XML dizisini parametre olarak alan [xml2dict.parse] yöntemi ile elde edilen [OrderedDict] türü;
- 6. satır: önceki türe [ordereddict2dict] yöntemini uygulayarak elde edilen [dict] türündeki sözlük. Burada 2. satırdaki orijinal sözlüğü buluruz;
Diğer tüm testler aynı şemaya göre oluşturulmuştur ve bir sözlükten XML dizesine, ardından bu XML dizesinden orijinal sözlüğe nasıl geçileceğini anlamanıza yardımcı olacaktır.
Diğer testler şu sonuçları vermektedir:
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. ve 27. satırlar önemli bir noktaya işaret etmektedir:
- 23. satır: [result] sözlüğündeki anahtarlara atanan değerler sayılardır;
- 26. satır: [ordereddict_dictionary1] sözlüğündeki anahtarlara atanan değerler karakter dizileridir. Bu, [xmltodict] kütüphanesinin bir zayıflığıdır. [parse] yöntemi yalnızca karakter dizileri üretir. Bunun nedeni kolayca anlaşılabilir:
- 25. satır: Sözlüğün oluşturulduğu XML dizesi. Bu dizede, XML etiketleri içinde kapsüllenmiş verilerin türüne dair hiçbir bilgi yoktur. [xmltodict.parse] en mantıklı olanı yapar: üretilen sözlükte her şeyi karakter dizisi olarak bırakır. [xmltodict]'e benzer, etiketlerde kapsanan verilerin türünün belirtildiği başka kütüphaneler de bulunmaktadır. Örneğin, [<enfants type=’int’>2</enfants>] etiketi bulunabilir;
- bunun sonucu olarak, [xmltodict] modülü tarafından üretilen bir sözlüğü kullanırken, ‘str’ türünden verinin gerçek türüne geçebilmek için sözlüğün kapsadığı veri türünü bilmek gerekir;
Şimdi, [OrderedDict] türünü [dict] türüne dönüştüren [ordereddict2dict] yöntemine odaklanalım:
# xmltodict.parse, XML'ten sözlüğe geçmek için. Sözlüğün bir kökü olmalıdır
# oluşturulan sözlük OrderedDict türündedir
# xmltodict.unparse, sözlükten XML'e geçmek için
def check(value):
# değer OrderedDict türündeyse, dönüştürülür
if isinstance(value, OrderedDict):
value2 = ordereddict2dict(value)
# değer liste türündeyse, dönüştürülür
elif isinstance(value, list):
value2 = list2list(value)
else:
# burada bir koleksiyon değil, basit bir tür söz konusudur
value2 = value
# yeni değer oluşturulur
return value2
def list2list(liste: list) -> list:
# yeni liste
newliste = []
# parametre listesinin öğelerini işleriz
for value in liste:
# value'yi yeni listeye ekleniyor
newliste.append(check(value))
# yeni listeyi döndürür
return newliste
def ordereddict2dict(ordered_dictionary: OrderedDict) -> dict:
# OrderedDict -> sözlüğü özyinelemeli olarak
newdict = {}
for key, value in ordered_dictionary.items():
# değeri yeni sözlüğe kaydedilir
newdict[key] = check(value)
# sözlüğü oluşturuyoruz
return newdict
- 30. satır: [ordereddict2dict] işlevi, [OrderedDict] türünde bir parametre alır;
- 32. satır: İşlev tarafından 37. satırda döndürülecek olan [dict] türündeki sözlük;
- satır 33: [ordered_dictionary] sözlüğündeki tüm (anahtar, değer) ikilileri incelenir;
- satır 35: Yeni sözlükte [key] anahtarı korunur, ancak ilişkili değer [value] değil, [check(value)]'tir. [check(value)] işlevi, [value] bir koleksiyon ise, [OrderedDict] türündeki tüm öğeleri bulmak ve bunları [dict] türüne dönüştürmekle görevlidir;
[check] yöntemi 5-16. satırlarda tanımlanmıştır:
- 5. satır: [value]'in türü bilinmediğinden, [value : type] yazılmamıştır;
- 7-8. satırlar: [value], [OrderedDict] türündeyse, az önce yorumladığımız [ordereddict2dict] işlevini özyinelemeli olarak çağırırız;
- 9-11. satırlar: Bir başka olası durum ise [value]'in bir liste olmasıdır. Bu durumda, 11. satırda, 19-27. satırlardaki [list2list] işlevi çağrılır;
- satır 12-14: Son durum ise [value]'in bir koleksiyon değil, basit bir tür olmasıdır. [check] fonksiyonu, [ordereddict2dict] ve [list2list] fonksiyonları gibi özyinelemelidir. Bu durumda, özyinelemenin duracağı durumu her zaman hesaba katmak gerektiği bilinmektedir. 12-14. satırlar bu durumu ele almaktadır;
- 16. satır: Özyinelemeli olarak çağrılsa da çağrılmasa da [check] fonksiyonu, 5. satırdaki [value] parametresinin yerine geçmesi gereken bir [valeur2] değeri üretir;
19-27. satırlarda tanımlanan [list2list] yöntemi, parametre olarak geçirilen bir listeyi işler. Listeyi tarar ve içinde bulunan tüm [OrderedDict] türündeki değerleri [dict] türüne dönüştürür.
- 21. satır: işlevin oluşturacağı yeni liste;
- 23-25. satırlar: listedeki tüm [value] değerleri taranır ve [check(value)] değeriyle değiştirilir. Bu [value] değeri de [list] veya [OrderedDict] türünde öğeler içerebilir. Bunlar, [check] özyinelemeli işlev tarafından doğru şekilde işlenecektir;