【问题标题】:how to modify the key of a nested Json如何修改嵌套Json的键
【发布时间】:2017-10-12 03:33:42
【问题描述】:

我正在尝试更新如下所示的 JSON 对象的键:

results = 
 {  
   'Game':12345,
   'stats':[  
      {  
         'detail':[  
            {  
               'goals':4,
               'refs':{  
                  'number':0

我目前正在手动更新每个键,如下所示

##update Game to newValue
results['newValue'] = results['Game']
    del results['Game']

## update nested key "goals" to "goals_against"
results['stats'][0]['detail'][0]['goals_against'] = results['stats'][0]['detail'][0]['goals']
    del results['stats'][0]['detail'][0]['goals']

必须有更好的方法,因为我发现自己必须更新结果中的多个键。例如,我还想将“number”键更新为“assis_ref”。

如果 json 文件是“简单”的,我知道如何更新密钥:即如果我可以这样做:

result['stats']['details']['refs']

然而,'stats' 和 'details' 需要 [0] 在它旁边,我假设它是我接下来要尝试的元素的索引。

【问题讨论】:

  • Rename a dictionary key的可能重复
  • @DJV 这有点不同,因为我试图更新的 JSon 对象中的键是深度嵌套的,有时它需要添加下一个节点的索引,例如 [0]跨度>
  • 您可以使用我发布为this answer 的代码来完成此任务。我几乎把这个问题作为那个问题的副本结束了,但我想它已经足够不同了,它应该有自己的答案,所以我会在几分钟内发布一些代码。
  • 用一些简单的结构来玩弄我的代码来感受一下它的作用可能是个好主意。但如果您需要进一步解释,请告诉我。
  • 如果我的回答对你有帮助,请考虑accepting它。

标签: python json dictionary


【解决方案1】:

我编写了一个递归函数来处理 json 对象中的转换键。对于深度嵌套的 json 尤其有用!

def walk_json(obj, key_transform):
    """
    Recurse over a json object modifying the keys according to the `key_transform` function.
    Returns a new json object with the modified keys.
    """
    assert isinstance(obj, dict), "walk_json expects obj to be of type dict"

    def _walk_json(obj, new):

        if isinstance(obj, dict):

            if isinstance(new, dict):
                for key, value in obj.items():

                    new_key = key_transform(key)

                    if isinstance(value, dict):
                        new[new_key] = {}
                        _walk_json(value, new=new[new_key])

                    elif isinstance(value, list):
                        new[new_key] = []
                        for item in value:
                            _walk_json(item, new=new[new_key])

                    else:  # take value as is
                        new[new_key] = value

            elif isinstance(new, list):
                new.append(_walk_json(obj, new={}))

        else:  # take object as is
            new.append(obj)

        return new

    return _walk_json(obj, new={})

这是一个过于简单的 json 对象的用法:

def my_key_transform(x):
    return x.upper()

my_obj = {"a": 1, "b": 2, "c": 3}

result = walk_json(my_obj, key_transform=my_key_transform)

result
{"A": 1, "B": 2, "C": 3}

【讨论】:

    【解决方案2】:

    导航和修改从 JSON 对象派生的深层嵌套对象可能会很痛苦。在Functions that help to understand json(dict) structure 中,我发布了允许您导航此类对象的代码。请阅读该答案中的解释。在这个答案中,我将展示如何使用该代码来修改此类对象中的字典键。

    简而言之,find_key 是一个递归生成器,它将找到具有给定名称的所有键。您可以使用next 函数来获取第一个(或唯一一个)匹配的名称。如果您需要使用多个具有相同名称的键,或者在 for 循环中调用 find_key

    find_key 产生的每个值都是dict 键的列表,列表索引需要到达所需的键。

    from json import dumps
    
    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])
    
    
    results = {
        "Game": 12345,
        "stats": [
            {
                "detail": [
                    {
                        "goals": 4,
                        "refs": {
                            "number": 0
                        }
                    }
                ]
            }
        ]
    }
    
    # Change oldkey to newkey      
    oldkey, newkey = 'goals', 'goals_against'
    # Find the first occurrence of the oldkey
    seq, val = next(find_key(results, oldkey))
    print('seq:', seq, 'val:', val)
    
    # Get the object that contains the oldkey 
    obj = results
    for k in seq[:-1]:
        obj = obj[k]
    
    # Change the key
    obj[newkey] = obj.pop(oldkey)
    
    print(dumps(results, indent=4))
    

    输出

    seq: ['stats', 0, 'detail', 0, 'goals'] val: 4
    {
        "Game": 12345,
        "stats": [
            {
                "detail": [
                    {
                        "refs": {
                            "number": 0
                        },
                        "goals_against": 4
                    }
                ]
            }
        ]
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-02
      • 1970-01-01
      • 2021-04-10
      • 2019-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多