我有一个相当嵌套的 json 结构,我想根据条件循环并修改 ID 值。理想情况下,不需要键名称,因为它们可以是可变的。我的想法是从顶层获取 Id 值,然后在整个 json 文件中搜索它并修改所有匹配的值,因为 ID 值始终相同。修改只是添加“_001”即可。但条件应该是它只能在“类型”的子目录中完成:“A”。 请参阅下面我正在处理的结构的示例 json:
{
"name1": "test1",
"Id": "12345678",
"data": [
{
"type": "A",
"Id1": "12345678",
"level2": {
"name2": "test2",
"Id2": "12345678",
"level3": {
"name3": "test3",
"Id3": "12345678"
},
"Edit": [
{
"Id": "12345678",
"Bla": "XXXXXXXXXX"
},
{
"Id": "12345678",
"Bla": "XXXXXXXXX"
}
]
}
},
{
"type": "B",
"id": "12345678",
"data": {
"Id": "12345678",
"Name": "test6"
}
}
]
}
我想循环这个 json 并替换 level1 内每个实例中的 Id 值,如果“type”等于“A”,否则不需要循环该特定的字典,这意味着不应该修改 ID。理想情况下,我会在顶层检查此 id 值,然后按值而不是键进行过滤,因为键可能会有所不同(请参阅最低级别的“source_id”)。因此,在示例 json 中,子词典中具有“type”:“A”的所有 Id 应被替换,而具有“type”:“B”的 Id 应保留。 作为输出,我想以相同的格式返回 json,只是更改了 ID。
到目前为止,我的代码仍然依赖于键名称的知识,并且没有捕获“编辑”字典列表中的 Id,因为我无法弄清楚如何在嵌套字典中搜索像“12345678”这样的 ID 值基于上述类型过滤器:
data = json.load(open("test.json"))
curr_id = data['Id']
new_id = curr_id + '_001')
for item in data['data']:
if item['type'] != 'B':
item['Id1'] = new_id
item['level2']['Id2'] = new_id
item['level2']['level3']['Id3'] = new_id
我会采用递归实现:
data['data']
level<current_level + 1>
中是否有
dict
这种方法的优点是你可以有可变数量的级别(例如级别 4 和级别 5 是可能的),并且它将正确处理它们。
代码:
import json
def replace_id_recursively(item, old_id, new_id, level=1):
"""
This method replaces the key on the current level
and calls itself to replace lower levels
"""
# This is an early-return: If the current
# type is not "A", move on (only for level 1)
# NOTE: is important to check level first since
# other levels do not have 'type'
if level == 1 and item['type'] != 'A':
return
# Change this level only if it has the old key
key = f"Id{level}"
print(f"Checking level {level} and key {key}")
if item[key] != old_id:
return
item[key] = new_id
# Check if this level has "Edit" - could be
# split in another function
if "Edit" in item:
print(f" - Handling Edit in level {level}")
for edit_item in item["Edit"]:
edit_item["Id"] = new_id
# Finally, before moving to the next item in
# the list, change any children
child = f"level{level + 1}"
if child in item:
replace_id_recursively(item[child], old_id, new_id, level + 1)
# MAIN
data = json.load(open("/tmp/data.json"))
old_id = data['Id']
new_id = f"{old_id}_001"
# For each item, check and replace IDs recursively
for item in data['data']:
replace_id_recursively(item, old_id, new_id, 1)
print(json.dumps(data, indent=4))
输出:
$ python3 ./tmp/so.py
Checking level 1 and key Id1
Checking level 2 and key Id2
- Handling Edit in level 2
Checking level 3 and key Id3
{
"name1": "test1",
"Id": "12345678",
"data": [
{
"type": "A",
"Id1": "12345678_001",
"level2": {
"name2": "test2",
"Id2": "12345678_001",
"level3": {
"name3": "test3",
"Id3": "12345678_001"
},
"Edit": [
{
"Id": "12345678_001",
"Bla": "XXXXXXXXXX"
},
{
"Id": "12345678_001",
"Bla": "XXXXXXXXX"
}
]
}
},
{
"type": "B",
"id": "12345678",
"data": {
"Id": "12345678",
"Name": "test6"
}
}
]
}
注释/假设:
这是一个简短的解决方案,用
your_dict = replace_recurse(your_dict)
调用它
def replace_recurse(it):
if isinstance(it, list):
return [
replace_recurse(entry) if entry.get('type', 'A') == 'A' else entry
for entry in it
]
if isinstance(it, dict):
return {
k: (v + '_001' if k.startswith('Id') else replace_recurse(v))
for k, v in it.items()
}
return it
这种方法盲目地递归到它找到的任何字典或列表中。除非递归在看到定义了
'type'
的字典不是 'A'
时停止。在递归过程中,'_001'
会附加到以 Id
开头的每个字段,因此此处为 Id
、Id1
和 Id2
。