如何使用 jsonpath-ng 从字典中删除节点?

问题描述 投票:0回答:3

在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
来做到这一点。

python jsonpath jsonpath-ng
3个回答
0
投票

这是我过去使用过的一个简单的解决方案:

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
        }
    }
]

0
投票

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}}]

-2
投票

如果您知道模式是固定的,您可以像这样简单地删除密钥

l = [
  { "top": { "lower": 1, "other": 1 } },
  { "top": { "lower": 2, "other": 4 } },
  { "top": { "lower": 3, "other": 9 } }
]

for d in l:
    del d["top"]["lower"]
© www.soinside.com 2019 - 2024. All rights reserved.