在Python中,我有一个字典列表,我想从列表中的每个字典中删除给定的节点。我对这些词典一无所知,除了它们都具有相同的(未知)模式。要删除的节点可能位于字典中的任何位置,并且由 JSONPath 表达式指定。
示例:
输入数据:
[
{ "top": { "lower": 1, "other": 1 } },
{ "top": { "lower": 2, "other": 4 } },
{ "top": { "lower": 3, "other": 9 } }
]
要删除的节点:
$.*.top.lower
预期结果:
[
{ "top": { "other": 1 } },
{ "top": { "other": 4 } },
{ "top": { "other": 9 } }
]
jsonpath
库我的第一次尝试是这样的:
from jsonpath import JSONPath
def remove_node_from_dict(data, node):
node_key = JSONPath(node).segments.pop()
for record in data:
del record[node_key]
但这仅当要删除的节点位于字典的顶层时才有效。 在研究解决方案时,我遇到了
jsonpath-ng
库,该库声称具有“更新或删除树中节点的能力”。但是,我找不到任何相关文档 - 它是如何完成的?
编辑:
基于this对相关问题的回答,我找到了一个至少适用于使用普通Python(而不是
jsonpath-ng
库)的简单路径(无过滤器等)的解决方案。这对于我的用例来说就足够了。我仍然想学习如何以更通用的方式使用 jsonpath-ng
来做到这一点。
这是我过去使用过的一个简单的解决方案:
import copy
import jsonpath_ng.ext as jp
def remove_matched_element(path, spec):
_new_spec = copy.deepcopy(spec)
jep = jp.parse(path)
for match in jep.find(spec):
_t_path = "$"
spec_path = _new_spec
spec_path_parent = None
for pe in str(match.full_path).split("."):
if _t_path != "$" and type(jp.parse(_t_path).find(_new_spec)[0].value) == list:
_t_path = f"{_t_path}{'.'}{pe}"
_idx = int(pe.replace("[", "").replace("]", ""))
spec_path_parent = spec_path
spec_path = spec_path[_idx]
elif _t_path != "$" and type(jp.parse(_t_path).find(_new_spec)[0].value) == dict and pe == "[0]":
keyp = list(jp.parse(_t_path).find(_new_spec)[0].value.keys())[0]
_idx = keyp
_t_path = f"{_t_path}.{keyp}"
spec_path_parent = spec_path
spec_path = spec_path[keyp]
else:
if type(spec_path) == list:
_idx = int(pe.replace("[", "").replace("]", ""))
_t_path = f"{_t_path}[{_idx}]"
else:
_idx = pe
_t_path = f"{_t_path}{'.'}{pe}"
spec_path_parent = spec_path
spec_path = spec_path[_idx]
spec_path_parent.pop(_idx)
return _new_spec
def test_soc_sol():
spec = [
{"top": {"lower": 1, "other": 1}},
{"top": {"lower": 2, "other": 4}},
{"top": {"lower": 3, "other": 9}}
]
print(
yaml.safe_dump(remove_matched_element("$..lower", spec)))
上面的代码结果是:
[
{
"top": {
"other": 1
}
},
{
"top": {
"other": 4
}
},
{
"top": {
"other": 9
}
}
]
jsonpath-ng 库实际上允许使用
.filter()
方法删除节点。
由于报告的问题#44,他们最近才在文档中添加示例。
我必须将您的 JSONPath 表达式更改为
$.[*].top.lower
:
from jsonpath_ng import parse
test_data = [
{ "top": { "lower": 1, "other": 1 } },
{ "top": { "lower": 2, "other": 4 } },
{ "top": { "lower": 3, "other": 9 } }
]
jsonpath_expr = parse("$.[*].top.lower")
jsonpath_expr.filter(lambda d: True, test_data)
print(test_data)
我得到了下一个输出:
[{'top': {'other': 1}}, {'top': {'other': 4}}, {'top': {'other': 9}}]
如果您知道模式是固定的,您可以像这样简单地删除密钥
l = [
{ "top": { "lower": 1, "other": 1 } },
{ "top": { "lower": 2, "other": 4 } },
{ "top": { "lower": 3, "other": 9 } }
]
for d in l:
del d["top"]["lower"]