首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Python中合并深度JSON文件

在Python中合并深度JSON文件
EN

Stack Overflow用户
提问于 2021-02-26 10:17:54
回答 1查看 891关注 0票数 0

我有两个JSON文件,一个包含一个完全定义的对象,包含多个嵌套的左旋体,另一个包含同一个对象的回退版本,其中只列出需要更改的元素。

文件1示例

代码语言:javascript
复制
{
  "toplevel": {
    "value": {
      "settings": [
        {
          "name": "A Default Value",
          "region": "US",
          "inner": {
            "name": "Another Default",
            "setting": "help"
          }
        }
      ]
    }
  }
}

文件2示例

代码语言:javascript
复制
{
  "toplevel": {
    "value": {
      "settings": [
        {
          "name": "A Real Value",
          "inner": {
            "name": "Another Real Value",
          }
        }
      ]
    }
  }
}

我想将文件2中的更新合并到文件1中。

我的输出应该像

代码语言:javascript
复制
{
  "toplevel": {
    "value": {
      "settings": [
        {
          "name": "A Real Value",
          "region": "US",
          "inner": {
            "name": "Another Real Value",
            "setting": "help"
          }
        }
      ]
    }
  }
}

到目前为止我已经试过了

代码语言:javascript
复制
f1 = json_load(file1)
f2 = json_load(file2)
f1['toplevel']['value']['settings'][0].update(f2['toplevel']['value']['settings'][0].items())

它非常适合顶级项,但显然它覆盖了整个“内部”对象,删除了其中的“设置”键。

有没有一种方法可以遍历整棵树,只替换非字典值?除了json和藏书之外,我没有访问外部库的权限(对于有序的dict)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-02-26 11:03:58

这有点取决于你想要什么

解决方案1

如果只想用新字典替换所有值,可以使用以下选项:

代码语言:javascript
复制
result = {**file_1, **file_2}  

from pprint import pprint
pprint(result)

这将导致:

代码语言:javascript
复制
{'toplevel': {'value': {'settings': [{'inner': {'name': 'Another Real Value'},
                                      'name': 'A Real Value'}]}}}

或者,您可以使用

代码语言:javascript
复制
file_1.update(file_2)

pprint(file_1)

这将导致相同的结果,但将更新file_1到位。

解决方案2

如果您只想更新嵌套中的特定键,并保留所有其他值,则可以使用递归进行此操作。在您的示例中,您使用的是dictliststr值。因此,我将使用相同的类型构建递归。

代码语言:javascript
复制
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 original
代码语言:javascript
复制
def 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

上面的内容可能有点难理解,但我会尽力解释。有两种方法,一种是合并两本字典,另一种是试图合并两个列表。

合并字典

为了合并这两个字典,我检查了更新字典中的所有键和值,因为这可能是两者中较小的一个。

第一个块在原始字典中放置新的键,这是更新最初字典中没有的值。

第二个块是更新嵌套值。在这里,我区分三种情况:

如果值是另一个

  1. ,则再次运行字典合并,但再运行一个级别。如果值是list (或tuple),则运行列表合并函数。如果值为d18dict >(或d19dict>、d20),则用更新后的值替换原始值。H 221G 222

合并列表

这比字典要复杂一些,因为列表没有我可以比较的顺序或键。因此,我不得不做一个沉重的假设,即list更新总是包含相同的元素,请参阅如何使用多个元素处理list的限制。

由于lists的长度相同,所以我可以假设列表的索引是匹配的。现在,为了检查所有的值是否相同,我们必须执行以下操作:

如果值是字典,则使用dictionaries.

  • If合并--值是list (或tuple)我们是列表合并。如果值为str (或int,d38),则重写原版。H 239G 240

结果

使用:

代码语言:javascript
复制
from pprint import pprint

pprint(update_dict(file_1, file_2))

最终结果将是:

代码语言:javascript
复制
{'toplevel': {'value': {'settings': [{'inner': {'name': 'Another Real Value',
                                                'setting': 'help'},
                                      'name': 'A Real Value',
                                      'region': 'US'}]}}}

注意,与第一个解决方案相比,'setting': 'help'和'region': 'US'}值现在仍然在原始字典中。

局限性

由于相同的长度约束,如果不想更新列表中的元素,则必须传递相同的元素类型,但必须为空。

关于如何忽略列表更新的示例:

代码语言:javascript
复制
... {'settings': [
          {}                      # do not update the first element.
          {'name': 'A new name'}  # update second element.
       ]
    }
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66383920

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档