¿Cómo recorrer completamente un complejo diccionario de profundidad desconocida?

La importación desde JSON puede obtener estructuras muy complejas y anidadas. Por ejemplo:

 {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 la anterior?

Aparte de algunos listados, en su mayoría hay diccionarios, la estructura puede ser aún más imbricada, por lo que necesito una solución general.

Puede usar un generador recursivo para convertir su diccionario en 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 

Vuelve

 [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'] 

Si solo necesita recorrer el diccionario, sugiero usar una función de walk recursivo que toma un diccionario y luego recursivamente recorre sus elementos. 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 ciertos criterios, eche un vistazo al módulo jsonpath .

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

Recomiendo esto especialmente si necesita codificar objetos que pertenecen a clases personalizadas en el json. Si tiene que hacer alguna operación que se pueda hacer también en una representación de cadena del json, considere también iterar JSONEncoder (). Iterencode

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

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

Para la estructura de datos de su ejemplo, puede crear las clases Program , VariableDeclaration , VariableDeclarator , Identifier , Literal y BinaryExpression , y luego usar algo como esto para su analizador:

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

Tal vez pueda 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 esta:

 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 

Alguna adición a la solución anterior (para manejar json incluyendo listas)

 #!/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)