我还没有找到有办法做到这一点。 假设我收到这样的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
,它有助于理解结构。
但有时数据量巨大,需要时间
有什么方法可以帮助我吗?
答案 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.load
或json.loads
返回的Python对象通常是一个字典,但它也可以是一个列表。我们将该对象作为find_key
arg传递给obj
生成器,以及我们想要找到的key
字符串。 find_key
然后根据需要调用iter_dict
或iter_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_list
与iter_dict
的工作方式类似,只是列表没有任何键,只包含值,因此我们不执行k == key
测试,我们只是递归到原始列表包含的任何dicts或列表中。
这个过程的最终结果是,当我们迭代find_key
时,我们得到(索引,值)对,其中每个indices
列表是dict键的序列,并且列表索引成功终止于带有我们所需键的dict项,value
是与该特定键关联的值。
如果您希望查看此代码的其他示例,请参阅how to modify the key of a nested Json和How can I select deeply nested key:values from dictionary in python。
另请查看我新的,更精简的show_indices
功能。