有助于理解 json(dict) 结构的函数
- 2025-03-26 09:10:00
- admin 原创
- 11
问题描述:
我还没有找到一种方法来做到这一点。假设我收到一个这样的 JSON 对象:
{'1_data':{'4_data':[{'5_data':'hooray'}, {'3_data':'hooray2'}], '2_data':[]}}
很难立即说,我应该如何从3_data 键中获取值:data['1_data']['4_data'][1]['3_data']
我知道pprint
,这有助于理解结构。但有时数据量很大,需要时间
有什么方法可以帮助我解决这个问题?
解决方案 1:
这是一组递归生成器,可用于搜索由字典和列表组成的对象。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
语句,例如替换
yield from iter_dict(obj, key, [])
和
for u in iter_dict(obj, key, []):
yield u
工作原理
要理解此代码的工作原理,您需要熟悉递归和 Python生成器。您可能还会发现此页面很有帮助:了解 Python 中的生成器;网上还有各种 Python 生成器教程。
json.load
或返回的 Python 对象json.loads
通常是一个字典,但也可以是一个列表。我们将该对象find_key
作为obj
参数传递给生成器,以及key
我们想要定位的字符串。find_key
然后根据需要调用iter_dict
或iter_list
,将对象、键和一个空列表传递给它们indices
,该列表用于收集字典键和指向我们想要的键的列表索引。
iter_dict
`d在其字典参数的顶层迭代每个 (k, v) 对。如果
k匹配我们要查找的键,则
indices生成当前列表并将
k相关值附加到该列表。由于
iter_dict是递归的,因此生成的 (索引列表,值) 对将传递到上一级递归,最终到达
find_key调用的代码
find_key。请注意,这是我们递归的“基本情况”:它是代码的一部分,用于确定此递归路径是否指向我们想要的键。如果递归路径从未找到与我们要查找的键匹配的键,则该递归路径将不会添加任何内容,
indices`并且它将终止而不生成任何内容。
如果当前v
是字典,那么我们需要检查它包含的所有 (键,值) 对。我们通过对 进行递归调用来实现iter_dict
这一点,传递v
其起始对象和当前indices
列表。如果当前v
是列表,我们改为调用iter_list
,并传递相同的参数。
iter_list
工作原理类似,iter_dict
只是列表没有任何键,它只包含值,所以我们不执行k == key
测试,我们只是递归到原始列表包含的任何字典或列表。
该过程的最终结果是,当我们迭代时,find_key
我们得到 (索引,值) 对,其中每个indices
列表都是字典键和列表索引的序列,这些序列成功终止于具有我们所需键的字典项,并且value
是与该特定键关联的值。
如果您想查看此代码的其他使用示例,请参阅如何修改嵌套 Json 的键和如何从 python 中的字典中选择深度嵌套的键:值。
还请看一下我的新的、更简化的show_indices
功能。