2012-09-20 19 views
30

La importación desde JSON puede obtener estructuras muy complejas y anidadas. Por ejemplo:Python: ¿Cómo atravesar por completo un diccionario complejo de profundidad desconocida?

{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'} 

¿Cuál es la forma recomendada para caminar estructuras complejas como el anterior?

Aparte de algunas listas, en su mayoría hay diccionarios, la estructura se puede volver aún más imbricada, así que necesito una solución general.

+1

¿Qué está tratando de hacer con el diccionario? – nneonneo

+1

¿Qué quiere decir con "caminar"? –

Respuesta

30

Puede usar un generador recursivo para convertir su diccionario a listas planas.

def dict_generator(indict, pre=None): 
    pre = pre[:] if pre else [] 
    if isinstance(indict, dict): 
     for key, value in indict.items(): 
      if isinstance(value, dict): 
       for d in dict_generator(value, [key] + pre): 
        yield d 
      elif isinstance(value, list) or isinstance(value, tuple): 
       for v in value: 
        for d in dict_generator(v, [key] + pre): 
         yield d 
      else: 
       yield pre + [key, value] 
    else: 
     yield indict 

Devuelve

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

De todas las soluciones que estoy leyendo, esta es la más fácil y funciona bien :-) Gracias Bryukhanov Valentin –

+1

Ojalá pudiera darle más de 10, jaja :-) –

+3

Gran solución, pero tiene un error, "[tecla] + pre" debe ser "pre + [tecla]". De lo contrario, las listas devueltas revolverán el camino. –

32

Si solo necesita recorrer el diccionario, le sugiero que use una función recursiva walk que toma un diccionario y recorre sus elementos recursivamente. Algo como esto:

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 también desea buscar elementos, o consultar varios elementos que pasan a ciertos criterios, echar un vistazo al módulo jsonpath.

+9

Esto solo funciona para diccionarios directamente anidados. En la estructura de datos de ejemplo, había varios valores de diccionario que eran listas de otros diccionarios. Se necesitará alguna lógica adicional para manejarlos (como recursión dentro de una lista de comprensión o expresión del generador). Para que las cosas funcionen correctamente, probablemente necesite utilizar su conocimiento del significado de los datos, como el hecho de que todos los diccionarios tienen una clave "tipo". – Blckknght

+0

También debe tener cuidado de verificar ciclos. p.ej.: foo = {}; foo ['bar'] = foo te hará explotar. Su función de caminar debe mantener una lista de los objetos que ha visto y no descender. (podría pasar la lista a cada llamada sucesiva) – Cory

4

Si conoce el significado de los datos, es posible que desee crear una función parse para convertir los contenedores anidados en un árbol de objetos de tipos personalizados. Luego usaría los métodos de esos objetos personalizados para hacer lo que necesite con los datos.

Para su estructura de datos ejemplo, es posible crear Program, VariableDeclaration, VariableDeclarator, Identifier, Literal y BinaryExpression clases, a continuación, utilizar algo como esto para su programa de análisis:

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.") 
4

En lugar de escribir su propio analizador, dependiendo En la tarea, puede extender codificadores y decodificadores desde el módulo de biblioteca estándar json.

Lo recomiendo especialmente si necesita codificar objetos pertenecientes a clases personalizadas en el json. . Si tiene que hacer alguna operación que podría hacerse también en una representación de cadena del JSON, considere también la iteración JSONEncoder() iterencode

Por tanto la referencia es http://docs.python.org/2/library/json.html#encoders-and-decoders

1

Tal vez puede ayudar:

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) 

producirá una salida como ésta:

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 
Cuestiones relacionadas