如何完整遍历未知深度的复杂字典?

Edu*_*scu 46 python json dictionary python-2.7

从中导入JSON可以获得非常复杂和嵌套的结构.例如:

{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'}
Run Code Online (Sandbox Code Playgroud)

如上所述,走复杂结构的推荐方法是什么?

除了一些列表,主要是字典,结构可以变得更加重叠,所以我需要一个通用的解决方案.

Val*_*nov 47

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

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
Run Code Online (Sandbox Code Playgroud)

它回来了

[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']
Run Code Online (Sandbox Code Playgroud)

  • 很好的解决方案,但你有一个错误,"[key] + pre"应该是"pre + [key]".否则,您返回的列表会混淆路径. (6认同)
  • 在我正在阅读的所有解决方案中,这是最简单且效果很好的:-) 谢谢 Bryukhanov Valentin (2认同)

Han*_*hen 46

如果你只需要走字典,我建议使用一个递归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
Run Code Online (Sandbox Code Playgroud)

如果您还想搜索元素或查询通过某些条件的多个元素,请查看jsonpath模块.

  • 这仅适用于直接嵌套的词典.在示例数据结构中,有几个字典值是其他字典的列表.将需要一些额外的逻辑来处理这些(例如在列表推导或生成器表达式内递归).为了使工作正常,您可能需要使用您对数据含义的了解,例如所有字典都具有"类型"键的事实. (14认同)

dan*_*nza 8

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

我推荐这个,特别是如果你需要将属于自定义类的对象编码到json中.如果你必须做一些可以在json的字符串表示上完成的操作,也可以考虑迭代JSONEncoder().iterencode

对于这两个参考是http://docs.python.org/2/library/json.html#encoders-and-decoders


Fab*_*los 6

也许可以帮助:

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)
Run Code Online (Sandbox Code Playgroud)

将产生这样的输出:

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 
Run Code Online (Sandbox Code Playgroud)


小智 6

要遍历/映射整个 JSON 结构,可以使用以下代码:

def walk(node, key):
    if type(node) is dict:
        return {k: walk(v, k) for k, v in node.items()}
    elif type(node) is list:
        return [walk(x, key) for x in node]
    else:
        return YourFunction(node, key)

def YourFunction(node, key):
    if key == "yourTargetField":   # for example, you want to modify yourTargetField
        return "Modified Value"
    return node # return existing value
Run Code Online (Sandbox Code Playgroud)

这将遍历您的整个 json 结构并通过您的函数运行每个叶子(端点键值对)。您的函数返回修改后的值。整个 walk 函数将按顺序为您提供一个新的、经过处理的对象。


Blc*_*ght 5

如果您知道数据的含义,则可能需要创建一个parse函数来将嵌套容器转换为自定义类型对象的树.然后,您可以使用这些自定义对象的方法来执行数据所需的任何操作.

对于示例数据结构,您可以创建Program,VariableDeclaration,VariableDeclarator,Identifier,LiteralBinaryExpression类,然后使用类似这样为您解析:

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.")
Run Code Online (Sandbox Code Playgroud)


Bri*_*eau 5

如果接受的答案适合您,但您还想要一个完整的、有序的路径,其中包含嵌套数组的数字索引,那么这种细微的变化将起作用:

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,  pre + [key]):
                    yield d
            elif isinstance(value, list) or isinstance(value, tuple):
                for k,v in enumerate(value):
                    for d in dict_generator(v, pre + [key] + [k]):
                        yield d
            else:
                yield pre + [key, value]
    else:
        yield indict
Run Code Online (Sandbox Code Playgroud)