web-dev-qa-db-fra.com

Comment parcourir complètement un dictionnaire complexe de profondeur inconnue?

L'importation depuis JSON peut obtenir des structures très complexes et imbriquées. Par exemple:

{u'body': [{u'declarations': [{u'id': {u'name': u'i',
                                       u'type': u'Identifier'},
                               u'init': {u'type': u'Literal', u'value': 2},
                               u'type': u'VariableDeclarator'}],
            u'kind': u'var',
            u'type': u'VariableDeclaration'},
           {u'declarations': [{u'id': {u'name': u'j',
                                       u'type': u'Identifier'},
                               u'init': {u'type': u'Literal', u'value': 4},
                               u'type': u'VariableDeclarator'}],
            u'kind': u'var',
            u'type': u'VariableDeclaration'},
           {u'declarations': [{u'id': {u'name': u'answer',
                                       u'type': u'Identifier'},
                               u'init': {u'left': {u'name': u'i',
                                                   u'type': u'Identifier'},
                                         u'operator': u'*',
                                         u'right': {u'name': u'j',
                                                    u'type': u'Identifier'},
                                         u'type': u'BinaryExpression'},
                               u'type': u'VariableDeclarator'}],
            u'kind': u'var',
            u'type': u'VariableDeclaration'}],
 u'type': u'Program'}

Quelle est la façon recommandée de marcher sur des structures complexes comme ci-dessus?

En dehors de quelques listes, il existe principalement des dictionnaires, la structure peut devenir encore plus imbriquée, j'ai donc besoin d'une solution générale.

44
Eduard Florinescu

Si vous avez seulement besoin de parcourir le dictionnaire, je vous suggère d'utiliser une fonction récursive walk qui prend un dictionnaire puis parcourt récursivement ses éléments. Quelque chose comme ça:

def walk(node):
    for key, item in node.items():
        if item is a collection:
            walk(item)
        else:
            It is a leaf, do your thing

Si vous souhaitez également rechercher des éléments ou interroger plusieurs éléments répondant à certains critères, jetez un œil au module jsonpath .

44
Hans Then

Au lieu d'écrire votre propre analyseur, selon la tâche, vous pouvez étendre les encodeurs et décodeurs à partir du module de bibliothèque standard json.

Je le recommande surtout si vous devez encoder des objets appartenant à des classes personnalisées dans le json. Si vous devez effectuer une opération qui pourrait être effectuée également sur une représentation sous forme de chaîne du json, pensez également à itérer JSONEncoder ().

Pour les deux, la référence est http://docs.python.org/2/library/json.html#encoders-and-decoders

8
danza

Si vous connaissez la signification des données, vous souhaiterez peut-être créer une fonction parse pour transformer les conteneurs imbriqués en une arborescence d'objets de types personnalisés. Vous utiliseriez ensuite les méthodes de ces objets personnalisés pour faire tout ce que vous devez faire avec les données.

Pour votre exemple de structure de données, vous pouvez créer Program, VariableDeclaration, VariableDeclarator, Identifier, Literal et BinaryExpression classes, puis utilisez quelque chose comme ça pour votre analyseur:

def parse(d):
    t = d[u"type"]

    if t == u"Program":
        body = [parse(block) for block in d[u"body"]]
        return Program(body)

    else if t == u"VariableDeclaration":
        kind = d[u"kind"]
        declarations = [parse(declaration) for declaration in d[u"declarations"]]
        return VariableDeclaration(kind, declarations)

    else if t == u"VariableDeclarator":
        id = parse(d[u"id"])
        init = parse(d[u"init"])
        return VariableDeclarator(id, init)

    else if t == u"Identifier":
        return Identifier(d[u"name"])

    else if t == u"Literal":
        return Literal(d[u"value"])

    else if t == u"BinaryExpression":
        operator = d[u"operator"]
        left = parse(d[u"left"])
        right = parse(d[u"right"])
        return BinaryExpression(operator, left, right)

    else:
        raise ValueError("Invalid data structure.")
5
Blckknght

Peut-être peut aider:

def walk(d):
    global path
      for k,v in d.items():
          if isinstance(v, str) or isinstance(v, int) or isinstance(v, float):
            path.append(k)
            print "{}={}".format(".".join(path), v)
            path.pop()
          Elif v is None:
            path.append(k)
            ## do something special
            path.pop()
          Elif isinstance(v, dict):
            path.append(k)
            walk(v)
            path.pop()
          else:
            print "###Type {} not recognized: {}.{}={}".format(type(v), ".".join(path),k, v)

mydict = {'Other': {'Stuff': {'Here': {'Key': 'Value'}}}, 'root1': {'address': {'country': 'Brazil', 'city': 'Sao', 'x': 'Pinheiros'}, 'surname': 'Fabiano', 'name': 'Silos', 'height': 1.9}, 'root2': {'address': {'country': 'Brazil', 'detail': {'neighbourhood': 'Central'}, 'city': 'Recife'}, 'surname': 'My', 'name': 'Friend', 'height': 1.78}}

path = []
walk(mydict)

Produira une sortie comme celle-ci:

Other.Stuff.Here.Key=Value 
root1.height=1.9 
root1.surname=Fabiano 
root1.name=Silos 
root1.address.country=Brazil 
root1.address.x=Pinheiros 
root1.address.city=Sao 
root2.height=1.78 
root2.surname=My 
root2.name=Friend 
root2.address.country=Brazil 
root2.address.detail.neighbourhood=Central 
root2.address.city=Recife 
4
Fabiano Silos

Quelques ajouts à la solution ci-dessus (pour gérer json, y compris les listes)

#!/usr/bin/env python

import json

def walk(d):
   global path
   for k,v in d.items():
      if isinstance(v, str) or isinstance(v, int) or isinstance(v, float):
         path.append(k)
         print("{}={}".format(".".join(path), v)) 
         path.pop()
      Elif v is None:
         path.append(k)
         # do something special
         path.pop()
      Elif isinstance(v, list):
         path.append(k)
         for v_int in v:
            walk(v_int)
         path.pop()
      Elif isinstance(v, dict):
         path.append(k)
         walk(v)
         path.pop()
      else:
         print("###Type {} not recognized: {}.{}={}".format(type(v), ".".join(path),k, v))

with open('abc.json') as f:
   myjson = json.load(f)

path = []
walk(myjson)
1
BurAk