2012-09-20 106 views
30

JSON导入可以得到非常复杂和嵌套的结构。 例如:Python:如何完全遍历未知深度的复杂字典?

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

什么是走复杂的结构,如上述建议的方法是什么?

除了少数列表之外,主要有字典,结构可以变得更加叠加,所以我需要一个通用的解决方案。

+1

什么是你想用的字典呢? – nneonneo

+1

“散步”是什么意思? –

回答

30

您可以使用递归生成器将字典转换为平面列表。

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 

它返回

[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

在我阅读的所有解决方案中,这是最简单的,并且效果很好:-)谢谢Bryukhanov Valentin –

+1

我希望我能给你10+,lol :-) –

+3

很好的解决方案,但你有错误,“[key] + pre”应该是“pre + [key]”。否则,你的返回列表混乱的道路。 –

32

如果你只需要走字典,我会建议使用递归walk函数,它需要一个字典,然后递归遍历它的元素。事情是这样的:

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

如果你也想搜索的元素,或查询通过某些标准的几个元素,看看在jsonpath模块。

+9

这只适用于直接嵌套字典。在示例数据结构中,有几个字典值是其他字典的列表。一些额外的逻辑将需要处理这些(例如在列表理解或生成器表达式中递归)。为了使事情正确,您可能需要使用数据含义的知识,例如所有字典都有“类型”键。 – Blckknght

+0

您还应该小心检查周期。例如:foo = {}; foo ['bar'] = foo会炸毁你。你的散步功能应该保留一个列表,而不是它所看到的对象。 (你可以将这个列表传递给每个连续的调用) – Cory

4

如果您知道数据的含义,你可能希望创建一个parse功能打开嵌套容器到自定义类型的对象树。然后,您可以使用这些自定义对象的方法来完成您需要处理的任何数据。

对于示例数据结构,您可以创建ProgramVariableDeclarationVariableDeclaratorIdentifierLiteralBinaryExpression类,然后使用类似这样的解析器:

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

而不是写自己的解析器,这取决于在任务上,您可以从标准库json模块扩展编码器和解码器。

如果您需要将属于自定义类的对象编码到json中,我特别推荐。如果你必须做一些操作可能也做对的JSON字符串表示,还要考虑迭代JSONEncoder()iterencode

对于这两种基准是http://docs.python.org/2/library/json.html#encoders-and-decoders

1

也许可以帮助:

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) 

会产生输出这样的:

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