在Python中查询和更新JSON

2024-06-16 00:35:53 发布

您现在位置:Python中文网/ 问答频道 /正文

我需要能够动态查询JSON对象,然后用值更新或附加它。注意,由于需要查询和更新任意一组值,因此标准json包不适合此任务。我发现了以下支持查询JSON的包:

  1. JsonPath_rw
  2. objectpath
  3. jmespath

但是,似乎只支持查询数据(请更正,如果我误解!),不更新或追加。例如,给定以下JSON:

{
  "people": [
    {
      "general": {
        "id": 100,
        "age": 20
      },
      "history": {
      }
    },
    {
      "general": {
        "id": 101,
        "age": 30
      },
      "history": {
      }
    },
    {
      "general": {
        "id": 100,
        "age": 30
      },
      "history": {
      }
    }
  ]
}

如果我想附加一个嵌套在“people”下的新“general”字段,并将id值从“id”更新为“identifier”,那么如何在Python中使用查询框架实现这一点,使其看起来像:

{
  "people": [
    {
      "general": {
        "identifier": 100,
        "age": 20
      },
    {
      "general": {
        "identifier": 100,
        "age": 20
      },
      "history": {
      }
    },
    {
      "general": {
        "identifier": 101,
        "age": 30
      },
      "history": {
      }
    },
    {
      "general": {
        "identifier": 100,
        "age": 30
      },
      "history": {
      }
    }
  ]
}

Tags: 对象idjsonage标准动态peoplehistory
2条回答

JSON(顾名思义)是一种表示JavaScript对象的方法。要进行操作,最合适的方法是将该表示解析为一个实际对象,对其进行操作,然后(如果需要)为该更新对象创建一个新的JSON表示。(事实上,我猜这些查询包正是这样做的,可能只需要足够的对象来满足查询。)

正如Scott所指出的,这里的对象并不是严格意义上的“JSON对象”。它是一个非常普通的Python dict,包含一个非常普通的列表,其中包含非常普通的dict,因此您可以使用普通迭代/索引赋值等操作它,而不需要框架。你知道吗

d = {
  "people": [
    {
      "general": {
        "id": 100,
        "age": 20
      },
      "history": {
      }
    },
    {
      "general": {
        "id": 101,
        "age": 30
      },
      "history": {
      }
    },
    {
      "general": {
        "id": 100,
        "age": 30
      },
      "history": {
      }
    }
  ]
}

#add new person
d["people"].insert(0, {
    "general": {
        "id": 100,
        "age": 20,
    },
    "history": {}
})

#copy `id` over to `identifier` for each person,
#and delete `id`
for person in d["people"]:
    person["general"]["identifier"] = person["general"]["id"]
    del person["general"]["id"]

print(d)

结果:

{'people': [{'general': {'age': 20, 'identifier': 100}, 'history': {}}, {'general': {'age': 20, 'identifier': 100}, 'history': {}}, {'general': {'age': 30, 'identifier': 101}, 'history': {}}, {'general': {'age': 30, 'identifier': 100}, 'history': {}}]}

加上空格,你就会得到

{
    'people': [
        {
            'general': {
                'age': 20, 
                'identifier': 100
            }, 
            'history': {}
        }, 
        {
            'general': {
                'age': 20, 
                'identifier': 100
            }, 
            'history': {}
        }, 
        {
            'general': {
                'age': 30, 
                'identifier': 101
            }, 
            'history': {}
        }, 
        {
            'general': {
                'age': 30, 
                'identifier': 100
            }, 
            'history': {}
        }
    ]
}

当然,只有知道对象的结构,这种方法才有效。如果发送此数据的人可以随时更改结构,那么您的代码很可能会立即中断。你知道吗

我认为您希望的是某种“智能解析器”,它可以解释具有任意结构的对象,并理解每个组件的概念含义。据我所知,没有这样的库存在,因为它需要人类水平的智能(或更好的)来对简单案例以外的任何事情做出有效的猜测。你知道吗

…也就是说,只要您能够对数据做出一定的保证,您就可以处理结构变化的某个级别。假设总是有一个“people”键需要附加,并且总是有一个“id”键需要重命名。如果这些事实保持不变,那么你就可以浏览字典,找到你需要的对象,不管它们在哪里。你知道吗

import copy

def find_key_item_pairs(obj, criteria):
    if isinstance(obj, dict):
        for key_and_value in obj.items():
            if criteria(key_and_value):
                yield key_and_value
            else:
                value = key_and_value[1]
                yield from find_key_item_pairs(value, criteria)
    elif isinstance(obj, list):
        for item in obj:
            yield from find_key_item_pairs(item, criteria)

d = {
  "people": [
    {
      "general": {
        "id": 100,
        "age": 20
      },
      "history": {
      }
    },
    {
      "general": {
        "id": 101,
        "age": 30
      },
      "history": {
      }
    },
    {
      "general": {
        "id": 100,
        "age": 30
      },
      "history": {
      }
    }
  ]
}

#dynamically locate all people lists
for _, people_list in find_key_item_pairs(d, lambda kv: kv[0] == "people" and isinstance(kv[1], list)):
    #duplicate the first entry and insert
    people_list.insert(0, copy.deepcopy(people_list[0]))

#dynamically locate all dicts containing "id"
for _, person in find_key_item_pairs(d, lambda kv: isinstance(kv[1], dict) and "id" in kv[1]):
    #swap out "id" for "identifier"
    person["identifier"] = person["id"]
    del person["id"]

print(d)

相关问题 更多 >