2012-09-20 128 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