Je travaille avec un gros fichier xml dans lequel j'ai essayé d'extraire des clés et des valeurs. Les informations contenues dans ce fichier sont très sensibles, je ne peux donc pas les partager. J'ai commencé par utiliser la bibliothèque xml. Cependant, après des heures de frustration, j'ai découvert la bibliothèque xmltodict. J'ai utilisé cette bibliothèque pour convertir mon xml en dictionnaire (quelque chose que je connais beaucoup plus par rapport au xml).

import xmltodict

# convert xml to dictionary
dict_nested = xmltodict.parse(str_xml)

Maintenant que le xml est un dictionnaire, j'aimerais l'aplatir car il y a un grand nombre de niveaux (je ne sais pas combien de niveaux), tout en créant des noms de clés qui m'aident à tracer le chemin vers leur valeur correspondante. Ainsi, j'ai essayé:

from flatten_dict import flatten

# flatten dict_nested 
dict_flat = flatten(dict_nested)

Le résultat peut ressembler à ceci mais avec beaucoup plus de couches:

{'ID': '123',
 'info': [{'breed':'collie'}, 
          {'fur': [{'short':'no'}, 
                   {'color':[{'black':'no'},
                             {'brown':'yes'}]}]}]}

Cela a bien fonctionné car mes clés sont des tuples montrant le chemin des couches. Mes valeurs sont soit des chaînes (c'est-à-dire le résultat final que je recherche) ou des listes de type OrderedDict .

Étant donné que chaque dictionnaire de chaque liste doit être aplati et que je ne sais pas jusqu'où cela va, j'essaie de trouver un moyen d'aplatir par programme tous les dictionnaires jusqu'à ce que toutes les clés correspondent à une seule valeur (c'est-à-dire, pas une liste ou un dictionnaire) .

Idéalement, la sortie ressemblerait à ceci:

{'ID':'123',
 'info_breed':'collie',
 'info_fur_short':'no',
 'info_fur_color_black':'no',
 'info_fur_color_brown':'yes'}

Désolé de ne pas pouvoir partager plus de ma sortie à cause des informations sensibles.

2
Aaron England 15 avril 2020 à 18:25

2 réponses

Meilleure réponse

Vous pouvez utiliser une approche récursive en tenant compte du fait que vos valeurs de dicts sont des chaînes ou des listes avec d'autres dicts:

dict_flat = {'ID': '123',
 'info': [{'breed':'collie'}, 
          {'fur': [{'short':'no'}, 
                   {'color':[{'black':'no'},
                             {'brown':'yes'}]}]}]}

def my_flatten(dict_flat, key_prefix=None):

    result = {}
    for k, v in dict_flat.items():
        key = f'{key_prefix}_{k}' if key_prefix is not None else k
        if isinstance(v, list):
            for d in v:
                result.update(my_flatten(d, key))
        else:
            result[key] = v
    return result

my_flatten(dict_flat)

Production:

{'ID': '123',
 'info_breed': 'collie',
 'info_fur_short': 'no',
 'info_fur_color_black': 'no',
 'info_fur_color_brown': 'yes'}
2
kederrac 15 avril 2020 à 16:05

Une autre approche consiste à créer un générateur qui produit des tuples clé / valeur. Vous pouvez simplement le transmettre au constructeur du dictionnaire:

d = {'ID': '123',
     'info': [{'breed':'collie'}, 
          {'fur': [{'short':'no'}, 
               {'color':[{'black':'no'},
                         {'brown':'yes'}]}]}]}

def flatten(obj, prefix=[]):
    if isinstance(obj, str):
        yield ('_'.join(prefix), obj)

    elif isinstance(obj, list):
        for o in obj:
            yield from flatten(o, prefix) 
    else:
        for k, v in obj.items():
            yield from flatten(v, prefix + [k])


dict(flatten(d))

Résultat:

{'ID': '123',
 'info_breed': 'collie',
 'info_fur_short': 'no',
 'info_fur_color_black': 'no',
 'info_fur_color_brown': 'yes'}

Cela évite de gérer le dictionnaire dans des fonctions qui, selon la façon dont vous aimez les choses, peuvent être plus faciles à raisonner.

1
Mark M 15 avril 2020 à 16:28