本文介绍如何将嵌套 json 对象中所有字段按完整路径格式(如 contact_status.term_list[0].t_list[1].s_code)提取为带层级语义的键名,并与对应值配对存入 list,解决原生 jackson jsonnode 递归遍历无法保留路径上下文的问题。
本文介绍如何将嵌套 json 对象中所有字段按完整路径格式(如 contact_status.term_list[0].t_list[1].s_code)提取为带层级语义的键名,并与对应值配对存入 list,解决原生 jackson jsonnode 递归遍历无法保留路径上下文的问题。
在处理复杂嵌套 JSON(尤其含多层对象与数组混合结构)时,仅获取字段名(如 "s_code")往往缺乏上下文,难以定位其真实归属路径。例如原始 JSON 中 s_code 出现在 term_list[0].t_list[1] 和 term_list[0].t_list[0] 两个不同位置,若不标注索引与层级,将导致歧义甚至数据错位。此时,标准 Jackson 的 JsonNode.fields() 或递归遍历方法无法自动构建带数组索引的完整路径(如 contact_status.term_list[0].t_list[1].s_code),需借助更高级的路径表达能力。
推荐方案:使用 Josson 库实现智能扁平化(Flatten)
Josson 是一个轻量、高性能的 Java JSON 处理库,专为 JSON 路径查询与结构转换设计。它内置 flatten() 函数,可将任意嵌套结构按指定分隔符(如 .)和数组索引格式(如 [0])展开为带完整路径的键值对,完美匹配本需求。
✅ 快速上手:添加依赖(Maven)
<dependency>
<groupId>com.octomix.josson</groupId>
<artifactId>josson</artifactId>
<version>1.8.1</version>
</dependency>✅ 核心代码示例:提取带路径的键与值
import com.octomix.josson.Josson;
import com.fasterxml.jackson.databind.JsonNode;
public class JsonPathExtractor {
public static void main(String[] args) {
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);
// Step 1: 对 contact_info[0] 执行路径扁平化(使用 '.' 分隔字段,'[0]' 格式标记数组索引)
Josson flattened = josson.getJosson("contact_info[0].flatten('.','[%d]')");
// Step 2: 提取所有带路径的键名(keys())
System.out.println("-- 带路径的字段名 --");
JsonNode keysNode = flattened.getNode("keys()");
keysNode.elements().forEachRemaining(elem ->
System.out.println(elem.asText())
);
// Step 3: 提取所有对应值(toArray())
System.out.println("\n-- 对应字段值 --");
JsonNode valuesNode = flattened.getNode("toArray()");
valuesNode.elements().forEachRemaining(elem ->
System.out.println(elem.toString())
);
// ✨ Bonus:一键生成 "key = value" 字符串列表
System.out.println("\n-- 键值对字符串列表 --");
JsonNode kvNode = josson.getNode(
"contact_info[0].flatten('.','[%d]').entries().concat(key,' = ',value)"
);
kvNode.elements().forEachRemaining(elem ->
System.out.println(elem.asText())
);
}
}? 输出结果解析
运行后将得到清晰、无歧义的路径化键值对:
-- 带路径的字段名 -- contact_id contact_status.exam_status contact_status.term_list[0].term_code contact_status.term_list[0].t_list[0].s_code contact_status.term_list[0].t_list[0].sexam_status contact_status.term_list[0].t_list[1].s_code contact_status.term_list[0].t_list[1].sexam_status -- 对应字段值 -- "Contact_001" 0 110 "12001" 0 "13001" 1 -- 键值对字符串列表 -- 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
⚠️ 注意事项与最佳实践
- 数组索引格式可控:flatten('.','[%d]') 中的 [%d] 可替换为 [%s] 或自定义模板(如 #%d),适配不同命名规范;
- 路径起点精准:务必明确指定起始节点(如 contact_info[0]),避免顶层多数组导致路径混乱;
- 类型一致性:flatten() 默认保留原始 JSON 类型;若需统一字符串化值,可在 concat() 中对 value 显式调用 .toString();
- 性能考量:Josson 内部基于 Jackson 构建,解析效率高;对于超大 JSON,建议配合流式 API 或分片处理;
- 替代方案对比:若受限于技术栈无法引入新依赖,可基于 Jackson 自行实现带路径参数的递归函数(传入 currentPath 字符串并动态拼接),但开发成本与维护性显著高于 Josson 方案。
通过 Josson 的 flatten(),开发者得以用一行声明式表达式替代数十行手动路径拼接逻辑,大幅提升 JSON 结构分析的准确性与开发效率。










