
本文介绍如何将嵌套 JSON 对象中所有字段的完整路径式键名(如 contact_status.term_list[0].t_list[1].s_code)及其对应值,精准提取并存入 List<String>,解决 Jackson JsonNode 原生遍历无法保留层级上下文的问题。
本文介绍如何将嵌套 json 对象中所有字段的**完整路径式键名**(如 `contact_status.term_list[0].t_list[1].s_code`)及其对应值,精准提取并存入 `list
在处理复杂嵌套 JSON(尤其是含数组与多层对象混合结构)时,仅用 Jackson 的 JsonNode.fields() 或递归遍历往往只能获取扁平化的字段名,丢失其所属路径信息——例如 "s_code" 无法区分是 term_list[0].t_list[0] 还是 term_list[0].t_list[1] 下的字段。这在配置校验、日志审计、动态表单生成等场景中会引发歧义甚至逻辑错误。
推荐采用专为 JSON 路径化操作设计的轻量库 Josson(基于 Jackson 构建,无额外依赖),它内置 flatten() 函数可自动将任意嵌套结构展开为带完整路径的键值对,语义清晰、代码简洁、开箱即用。
✅ 快速集成与使用步骤
-
添加 Maven 依赖(确保使用最新稳定版):
<dependency> <groupId>com.octomix.josson</groupId> <artifactId>josson</artifactId> <version>1.9.2</version> </dependency>
-
构建 Josson 实例并加载 JSON:
String json = "{\n" + " \"contact_info\": [{\n" + " \"contact_id\": \"Contact_001\",\n" + " \"contact_status\": {\n" + " \"exam_status\": 0,\n" + " \"term_list\": [{\n" + " \"term_code\": 110,\n" + " \"t_list\": [\n" + " {\"s_code\": \"12001\", \"sexam_status\": 0},\n" + " {\"s_code\": \"13001\", \"sexam_status\": 1}\n" + " ]\n" + " }]\n" + " }\n" + " }]\n" + "}"; Josson josson = Josson.fromJsonString(json); -
执行路径化展开并提取键值对
使用 flatten('.', '[%d]'):- '.' 表示对象层级分隔符(如 contact_status.term_code)
- '[%d]' 表示数组索引格式(生成 [0], [1] 等)
⚠️ 注意:flatten() 默认作用于单个对象;本例中需先定位到 contact_info[0](首个联系人)
// 提取「键名 = 值」格式的字符串列表(最常用)
JsonNode entriesNode = josson.getNode(
"contact_info[0].flatten('.','[%d]').entries().concat(key,' = ',value)"
);
List<String> keyValues = new ArrayList<>();
entriesNode.elements().forEachRemaining(elem ->
keyValues.add(elem.asText())
);
// 输出结果(已按路径精确标识)
keyValues.forEach(System.out::println);
/*
contact_id = Contact_001
contact_status.exam_status = 0
contact_status.term_list[0].term_code = 110
contact_status.term_list[0].t_list[0].s_code = 12001
contact_status.term_list[0].t_list[0].sexam_status = 0
contact_status.term_list[0].t_list[1].s_code = 13001
contact_status.term_list[0].t_list[1].sexam_status = 1
*/? 进阶用法:分离键名与值列表
若需分别获取 List<String> keys 和 List<String> values(例如用于元数据注册或字段白名单校验):
Josson flattened = josson.getJosson("contact_info[0].flatten('.','[%d]')");
// 获取所有路径化键名
List<String> keys = StreamSupport.stream(
flattened.getNode("keys()").elements().spliterator(), false)
.map(JsonNode::asText).collect(Collectors.toList());
// 获取所有原始值(JSON 字符串格式,含引号)
List<String> rawValues = StreamSupport.stream(
flattened.getNode("toArray()").elements().spliterator(), false)
.map(JsonNode::toString).collect(Collectors.toList());⚠️ 注意事项与最佳实践
- 数组索引一致性:flatten() 中的 [%d] 保证索引从 0 开始连续编号,与实际 JSON 结构严格对齐,无需手动维护下标。
- 类型保留:values() 返回的是原始 JSON 类型(数字不加引号,字符串带双引号),如需统一字符串化,可用 elem.asText() 替代 elem.toString()。
- 性能考量:Josson 内部复用 Jackson 解析器,对千级以内嵌套字段性能优异;超大规模结构建议结合流式处理或分片查询。
- 替代方案对比:若受限于技术栈无法引入新依赖,可基于 Jackson 手动实现带路径参数的递归遍历(需维护 currentPath 字符串并动态拼接 "[i]"),但代码复杂度与出错率显著升高,不推荐新手采用。
通过 Josson 的声明式路径操作,开发者得以用极少代码实现高精度 JSON 字段溯源,真正将“字段在哪一层、属于哪个数组元素”转化为可读、可存、可比的结构化信息——这是构建健壮 JSON 处理能力的关键一步。










