【问题标题】:Add or Remove element in a dict given the parent object keys without consider the schema在给定父对象键的情况下在字典中添加或删除元素而不考虑架构
【发布时间】:2021-12-22 23:38:27
【问题描述】:

我正在尝试找到一种方法来添加/删除一个或多个 JSON 对象中的一个或多个元素,给定这些元素的父对象键,而不管对象的架构定义如何。

让我们举个例子。 假设我们有以下 JSON 对象:

{
    "field1": "",
    "field2": "",
    "list1": [
        {
            "list1_field1": "",
            "list1_obj1": {
                "list1_obj1_field1": "",
            },
            "list1_field2": "",
        },
        {
            "list1_field1": "",
            "list1_obj1": {
                "list1_obj1_field1": "",
            },
            "list1_field2": "",
            "list1_field3": "",
            "list1_sublist1": [
                {
                    "list1_sublist1_field1": ""
                }
            ]
        }
      ]
}

现在,假设我想在“list1”的所有元素中的“list1_obj1”对象中添加一个新字段。然后,键将是“list1”和“list1_obj1”,新字段将是,例如,“list1_obj1_field2”。 p>

总而言之,在输入键“list1”和“list1_obj1”中,我想在这个嵌套级别添加或删除一个新字段,但是不考虑 JSON 对象的架构。

当然,假设JSON文件中存在“list1”和“list1_obj1”,如果去掉,“list1_obj1_field2”也存在。

现在,我正在努力解决的最大问题是考虑嵌套对象列表。 如果我不考虑这个限制,我可以实现类似以下线程12 中的解决方案。

然后,为了实现这一目标,我设想了一个类似以下的解决方案:

# Remove item from the json object
# Suppose the json object is stored in a variable called "json_object"
keys = "list1.list1_obj1.list1_obj1_field2".split(".")
item = json_object
for i,key in enumerate(keys):
  
  if isinstance(item,dict):
    print("it's a dict")
    if key in item.keys():
      print(item)
      if i == len(keys)-1:
        # last item, so we can remove it
      else:
        item = item[key]
        
  else:
    print("it's a list")
    # loop on the list and for each element remove the item

如果嵌套项目是一个列表,我认为我应该对其进行迭代,并为每个元素找到要删除的正确项目。但是,我发现此解决方案效率低下。 另外,我试图找出一种使函数递归的方法,但没有成功。

任何提示将不胜感激。

非常感谢

编辑 1:

我设法实现了第一个递归版本。

def remove_element(obj, keys, current_key=0):
  
  """
    obj: the item passed in the function. At the beginning it is the entire json object
    keys: list that represents the complete key path from the root to the interested field
    current_key: index which points to keys list elements
  """
  
  if isinstance(obj, dict):
    for k in obj.keys():
        if k == keys[current_key]:
          if isinstance(obj[k], dict):
            obj[k] = remove_element(obj[k], keys, current_key+1)
          elif isinstance(obj[k], list):
            for i in range(len(obj[k])):
                obj[k][i] = remove_element(obj[k][i],keys, current_key+1)
          else:
            obj[k] = ""
            
    return obj

目前,该函数不会删除所需的字段,而是仅将其设置为“”,因为如果我尝试删除它,我会得到 RuntimeError: dictionary changed size during iteration(删除 obj[k])。

改进之处在于,现在可以在不考虑架构的情况下访问字段。 但是,仍然无法删除它,并且只能访问没有子项的字段(不是列表或字典的所有内容)。

【问题讨论】:

    标签: python json recursion


    【解决方案1】:

    我终于设法实现了 add 和 remove 方法。 实际上,由于更新方法与删除方法非常相似(仅更改了1行代码),因此我将它们集成在一个函数中。

    def add(obj, keys, obj_copy, value, current_key=0):  
      """
      obj: the item passed in the function. At the beginning it is the entire json object
      keys: the complete key path from the root to the interested field
      obj_copy: copy of obj. obj is used to iterate, obj_copy is updated. 
                This is done to prevent the "update dictionary during a loop" Runtime Error  
      value: value used to add the desired field. It can be a primitive type, a dict or a list
      current_key: index which points to keys list 
      """
      
      if isinstance(obj, dict):
        for k in obj.keys():
          if current_key != (len(keys)-1):
            if k == keys[current_key]:
              if isinstance(obj[k], dict):
                obj_copy[k] = add(obj[k], keys, obj_copy[k], value, current_key+1)
              elif isinstance(obj[k], list):
                for i in range(len(obj[k])):
                    obj_copy[k][i] = add(obj[k][i], keys, obj_copy[k][i], value, current_key+1)
          else:
            obj_copy[keys[current_key]] = value
            break
                
      return obj_copy
    
    
    def update(obj, keys, obj_copy, function, value=None, current_key=0):
      
      """
      obj: the item passed in the function. At the beginning it is the entire json object
      keys: the complete key path from the root to the interested field
      obj_copy: copy of obj. obj is used to iterate, obj_copy is updated. 
                This is done to prevent the "update dictionary during a loop" Runtime Error  
      function: "delete" if you want to delete an item, "update" if you want to update it
       value: value used to update the desired field. It can be a primitive type, a dict or a list
      current_key: index which points to keys list 
      """
      
      if isinstance(obj, dict):
        for k in obj.keys():
          if k == keys[current_key]:
            if current_key != (len(keys)-1):
              if isinstance(obj[k], dict):
                obj_copy[k] = update(obj[k], keys, obj_copy[k], function, value, current_key+1)
              elif isinstance(obj[k], list):
                for i in range(len(obj[k])):
                    obj_copy[k][i] = update(obj[k][i], keys, obj_copy[k][i], function, value, current_key+1)
            else:
              if function == "delete":
                del obj_copy[k]
              else:
                obj_copy[k] = value
                
      return obj_copy
    

    我唯一不满意的是“add”和“update”几乎是除了 1 行代码以及需要切换这些 if 语句的顺序的事实:

    # update
    if k == keys[current_key]:
       if current_key != (len(keys)-1):
    
    # add
    if current_key != (len(keys)-1):
      if k == keys[current_key]:
    

    我期待着弄清楚如何优化解决方案。

    此外,为了使界面更简单,我实现了一个包装函数。这是它的工作原理

    # WRAPPER
    def update_element(obj: dict, keys: str, function: str, value=None):
      
      """
      Description:
        remove or update an element 
        
      Input:
        obj: the object passed in the function.
        keys: the complete key path from the root to the interested field. The fields have to be separated by "."
        function: "delete" if you want to delete an item, "update" if you want to update it, "add" if you want to add it
        value: value used to update the desired field. It can be a primitive type, a dict or a list
      """
      
      keys = keys.split(".")
      obj_copy = deepcopy(obj)
      
      if function == "add":
        output = add(obj, keys, obj_copy, value)
      elif function == "update" or function == "delete":
        output = update(obj, keys, obj_copy, function, value)
      else:
        return {"message": "error: no function recognized. Possible values are: 'add', 'delete' or 'update' "}
      
      return output
    

    例子:

    thisdict =  {
      "brand": "Ford",
      "model": "Mustang",
      "year": 1964
    }
    
    delete_output = update_element(thisdict, "model", "delete")
    update_output = update_element(thisdict, "model", "update", "Fiesta")
    add_output = update_element(thisdict, "used", "add", "yes")
    

    【讨论】:

      猜你喜欢
      • 2021-05-04
      • 1970-01-01
      • 2013-10-16
      • 1970-01-01
      • 2014-11-10
      • 2019-10-23
      • 2014-04-05
      • 1970-01-01
      • 2014-04-16
      相关资源
      最近更新 更多