我有两个JSON文件,一个包含一个完全定义的对象,包含多个嵌套的左旋体,另一个包含同一个对象的回退版本,其中只列出需要更改的元素。
文件1示例
{
"toplevel": {
"value": {
"settings": [
{
"name": "A Default Value",
"region": "US",
"inner": {
"name": "Another Default",
"setting": "help"
}
}
]
}
}
}文件2示例
{
"toplevel": {
"value": {
"settings": [
{
"name": "A Real Value",
"inner": {
"name": "Another Real Value",
}
}
]
}
}
}我想将文件2中的更新合并到文件1中。
我的输出应该像
{
"toplevel": {
"value": {
"settings": [
{
"name": "A Real Value",
"region": "US",
"inner": {
"name": "Another Real Value",
"setting": "help"
}
}
]
}
}
}到目前为止我已经试过了
f1 = json_load(file1)
f2 = json_load(file2)
f1['toplevel']['value']['settings'][0].update(f2['toplevel']['value']['settings'][0].items())它非常适合顶级项,但显然它覆盖了整个“内部”对象,删除了其中的“设置”键。
有没有一种方法可以遍历整棵树,只替换非字典值?除了json和藏书之外,我没有访问外部库的权限(对于有序的dict)
发布于 2021-02-26 11:03:58
这有点取决于你想要什么
解决方案1
如果只想用新字典替换所有值,可以使用以下选项:
result = {**file_1, **file_2}
from pprint import pprint
pprint(result)这将导致:
{'toplevel': {'value': {'settings': [{'inner': {'name': 'Another Real Value'},
'name': 'A Real Value'}]}}}或者,您可以使用
file_1.update(file_2)
pprint(file_1)这将导致相同的结果,但将更新file_1到位。
解决方案2
如果您只想更新嵌套中的特定键,并保留所有其他值,则可以使用递归进行此操作。在您的示例中,您使用的是dict、list和str值。因此,我将使用相同的类型构建递归。
def update_dict(original, update):
for key, value in update.items():
# Add new key values
if key not in original:
original[key] = update[key]
continue
# Update the old key values with the new key values
if key in original:
if isinstance(value, dict):
update_dict(original[key], update[key])
if isinstance(value, list):
update_list(original[key], update[key])
if isinstance(value, (str, int, float)):
original[key] = update[key]
return originaldef update_list(original, update):
# Make sure the order is equal, otherwise it is hard to compare the items.
assert len(original) == len(update), "Can only handle equal length lists."
for idx, (val_original, val_update) in enumerate(zip(original, update)):
if not isinstance(val_original, type(val_update)):
raise ValueError(f"Different types! {type(val_original)}, {type(val_update)}")
if isinstance(val_original, dict):
original[idx] = update_dict(original[idx], update[idx])
if isinstance(val_original, (tuple, list)):
original[idx] = update_list(original[idx], update[idx])
if isinstance(val_original, (str, int, float)):
original[idx] = val_update
return original上面的内容可能有点难理解,但我会尽力解释。有两种方法,一种是合并两本字典,另一种是试图合并两个列表。
合并字典
为了合并这两个字典,我检查了更新字典中的所有键和值,因为这可能是两者中较小的一个。
第一个块在原始字典中放置新的键,这是更新最初字典中没有的值。
第二个块是更新嵌套值。在这里,我区分三种情况:
如果值是另一个
list (或tuple),则运行列表合并函数。如果值为d18dict >(或d19dict>、d20),则用更新后的值替换原始值。H 221G 222合并列表
这比字典要复杂一些,因为列表没有我可以比较的顺序或键。因此,我不得不做一个沉重的假设,即list更新总是包含相同的元素,请参阅如何使用多个元素处理list的限制。
由于lists的长度相同,所以我可以假设列表的索引是匹配的。现在,为了检查所有的值是否相同,我们必须执行以下操作:
如果值是字典,则使用dictionaries.
If合并--值是list (或tuple)我们是列表合并。如果值为str (或int,d38),则重写原版。H 239G 240结果
使用:
from pprint import pprint
pprint(update_dict(file_1, file_2))最终结果将是:
{'toplevel': {'value': {'settings': [{'inner': {'name': 'Another Real Value',
'setting': 'help'},
'name': 'A Real Value',
'region': 'US'}]}}}注意,与第一个解决方案相比,'setting': 'help'和'region': 'US'}值现在仍然在原始字典中。
局限性
由于相同的长度约束,如果不想更新列表中的元素,则必须传递相同的元素类型,但必须为空。
关于如何忽略列表更新的示例:
... {'settings': [
{} # do not update the first element.
{'name': 'A new name'} # update second element.
]
}https://stackoverflow.com/questions/66383920
复制相似问题