有助于理解json(dict)结构的函数

时间:2017-01-21 09:49:02

标签: python

我还没有找到有办法做到这一点。 假设我收到这样的JSON对象:

{'1_data':{'4_data':[{'5_data':'hooray'}, {'3_data':'hooray2'}], '2_data':[]}}

很难立即说出,我应该如何从 3_data key 获得价值:data['1_data']['4_data'][1]['3_data']

我知道pprint,它有助于理解结构。 但有时数据量巨大,需要时间

有什么方法可以帮助我吗?

1 个答案:

答案 0 :(得分:8)

以下是一系列递归生成器,可用于搜索由dicts和列表组成的对象。 find_key生成一个元组,其中包含字典键列表和列表索引,这些索引会导致您传入的密钥;元组还包含与该键关联的值。因为它是一个生成器,如果对象包含多个匹配的键,它将找到所有匹配的键,如果需要的话。

def find_key(obj, key):
    if isinstance(obj, dict):
        yield from iter_dict(obj, key, [])
    elif isinstance(obj, list):
        yield from iter_list(obj, key, [])

def iter_dict(d, key, indices):
    for k, v in d.items():
        if k == key:
            yield indices + [k], v
        if isinstance(v, dict):
            yield from iter_dict(v, key, indices + [k])
        elif isinstance(v, list):
            yield from iter_list(v, key, indices + [k])

def iter_list(seq, key, indices):
    for k, v in enumerate(seq):
        if isinstance(v, dict):
            yield from iter_dict(v, key, indices + [k])
        elif isinstance(v, list):
            yield from iter_list(v, key, indices + [k])

# test

data = {
    '1_data': {
        '4_data': [
            {'5_data': 'hooray'},
            {'3_data': 'hooray2'}
        ], 
        '2_data': []
    }
}

for t in find_key(data, '3_data'):
    print(t)

<强>输出

(['1_data', '4_data', 1, '3_data'], 'hooray2')

要获取单个键列表,您可以将find_key传递给next函数。如果您想使用密钥列表来获取相关值,您可以使用简单的for循环。

seq, val = next(find_key(data, '3_data'))
print('seq:', seq, 'val:', val)

obj = data
for k in seq:
    obj = obj[k]
print('obj:', obj, obj == val)

<强>输出

seq: ['1_data', '4_data', 1, '3_data'] val: hooray2
obj: hooray2 True

如果密钥可能丢失,请给next一个适当的默认元组。例如:

seq, val = next(find_key(data, '6_data'), ([], None))
print('seq:', seq, 'val:', val)
if seq:
    obj = data
    for k in seq:
        obj = obj[k]
    print('obj:', obj, obj == val)

<强>输出

seq: [] val: None

请注意,此代码适用于Python 3.要在Python 2上运行它,您需要替换所有yield from语句,例如replace

yield from iter_dict(obj, key, [])

for u in iter_dict(obj, key, []):
    yield u

如何运作

要了解此代码的工作原理,您需要熟悉recursion和Python generators。您可能还会发现此页面有用:Understanding Generators in Python;还有在线提供的各种Python生成器教程。

json.loadjson.loads返回的Python对象通常是一个字典,但它也可以是一个列表。我们将该对象作为find_key arg传递给obj生成器,以及我们想要找到的key字符串。 find_key然后根据需要调用iter_dictiter_list,将对象,密钥和空列表indices传递给它们,用于收集字典键和列出导致我们想要的密钥的索引。

iter_dict遍历其d dict arg顶层的每个(k,v)对。如果k与我们正在查找的密钥匹配,那么当前indices列表会附加k,并附带相关值。因为iter_dict是递归的,所以得到的(索引列表,值)对会被传递到先前的递归级别,最终会升级到find_key,然后转到调用find_key的代码。请注意,这是&#34;基本情况&#34;我们的递归:它是代码的一部分,它决定了这个递归路径是否会导致我们想要的密钥。如果递归路径从未找到与我们要查找的密钥匹配的密钥,则该递归路径不会向indices添加任何内容,并且它将终止而不会产生任何内容。

如果当前v是一个字典,那么我们需要检查它包含的所有(键,值)对。我们通过对iter_dict进行递归调用来做到这一点,传递v是它的起始对象和当前的indices列表。如果当前v是一个列表,我们会调用iter_list,并传递相同的参数。

iter_listiter_dict的工作方式类似,只是列表没有任何键,只包含值,因此我们不执行k == key测试,我们只是递归到原始列表包含的任何dicts或列表中。

这个过程的最终结果是,当我们迭代find_key时,我们得到(索引,值)对,其中每个indices列表是dict键的序列,并且列表索引成功终止于带有我们所需键的dict项,value是与该特定键关联的值。

如果您希望查看此代码的其他示例,请参阅how to modify the key of a nested JsonHow can I select deeply nested key:values from dictionary in python

另请查看我新的,更精简的show_indices功能。