在给定父对象键的dict中添加或删除元素,而不考虑架构



我正试图找到一种方法,在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_obj1";对象的所有元素中list1";。然后,键将是">list1";以及">list1_obj1";并且新字段将是例如">list1_obj1_field2";。

总之,在输入中给定键">list1";以及">list1_obj1";我想在这个嵌套级别添加或删除一个新字段,但不考虑JSON对象的模式。

当然,假设是">list1";以及">list1_obj1";存在于JSON文件中,并且在移除的情况下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

目前,该函数不删除所需字段,但仅将其设置为"&";,因为如果我尝试删除字典(del obj[k](,我会得到一个RuntimeError:字典在迭代过程中更改了大小

改进之处在于,现在可以在不考虑模式的情况下访问某个字段。然而,仍然不可能删除它,并且可以只访问没有子项的字段(不是列表或dict的所有字段(。

我终于实现了add和remove方法。事实上,由于update方法与remove方法非常相似(只有一行代码更改(,我将它们集成在一个函数中。

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

我唯一不满意的是;添加";以及";"更新";除了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")

最新更新