使用Newtonsoft.Json按JSON Path白名单裁剪JSON并保留原层级结构
实现方案
核心逻辑
- 先把所有白名单JSON Path拆解为层级路径段,同时将每一级父路径也标记为合法,确保匹配节点的上层结构不会被误删
- 递归遍历原始JSON,仅保留落在合法路径范围内的节点,最终输出的结构和原始JSON的层级、键顺序完全一致
- 区别于普通JSON Path直接返回匹配值的逻辑,该方案会自动保留完整的父级路径结构
代码实现(Python,无第三方依赖,适配固定路径白名单)
import json from typing import Dict, List, Set def split_json_path(path: str) -> List[str]: # 拆解 $.a.b.c 格式的JSON Path为路径段列表 return path.lstrip("$.") .split(".") def collect_allowed_prefixes(whitelist: List[str]) -> Set[tuple]: # 收集所有合法路径前缀(含完整匹配路径),用于判断节点是否需要保留 allowed = set() for path_expr in whitelist: segments = split_json_path(path_expr) for i in range(1, len(segments) + 1): allowed.add(tuple(segments[:i])) return allowed def filter_json_by_whitelist(source: Dict, allowed_prefixes: Set[tuple], current_path: tuple = ()) -> Dict: filtered_node = {} for key, value in source.items(): new_path = current_path + (key,) if new_path not in allowed_prefixes: continue if isinstance(value, dict): child_node = filter_json_by_whitelist(value, allowed_prefixes, new_path) # 仅当子节点有有效内容时保留当前键,避免生成空对象 if child_node: filtered_node[key] = child_node else: filtered_node[key] = value return filtered_node # 测试运行 if __name__ == "__main__": # 输入原始JSON source_data = { "path1": { "path1Inner1": {"id": "id1"}, "path1Inner2": {"id": "id2"} }, "path2": { "path2Inner1": {"id": "id3"}, "path2Inner2": {"id": "id4", "key": "key4"} } } # 输入白名单 path_whitelist = [ "$.path1.path1Inner1", "$.path2.path2Inner2.key" ] allowed_paths = collect_allowed_prefixes(path_whitelist) result = filter_json_by_whitelist(source_data, allowed_paths) # 输出结果 print(json.dumps(result, indent=4, ensure_ascii=False))
输出结果
{ "path1": { "path1Inner1": { "id": "id1" } }, "path2": { "path2Inner2": { "key": "key4" } } }
拓展说明
如果你的白名单需要支持通配符、数组索引、条件过滤等复杂JSON Path语法,可以引入jsonpath-ng库解析路径表达式,先找到所有匹配的节点全路径,再按上面的路径前缀匹配逻辑过滤即可。
内容的提问来源于stack exchange,提问作者ademchenko
相关产品推荐
相关产品推荐

