
本文介绍如何在不依赖预知键名的前提下,动态解析任意结构的json字符串,对其中值为json对象(以`{`开头)的字段自动反序列化并拼接其所有内部值,最终生成标准化结果。适用于数据库中存储格式不统一、键名海量且不可枚举的场景。
在实际数据处理中,尤其是从数据库读取JSON列时,常遇到结构高度动态的情况:顶层键名数量庞大(如上千个)、命名无规律、且部分值本身又是JSON字符串(如 "be003": "{\"1\":\"ABC\",\"2\":\"DEF\",\"3\":\"DEF\"}")。此时硬编码键名(如 optString("ad002") 或 getJSONObject("be003"))不仅维护成本高,更违背可扩展性原则。
推荐使用轻量级、表达力强的 JSON 转换库 Josson(GitHub 地址)实现声明式处理。它支持链式 JSONPath 风格查询与转换,无需手动遍历 Map 或递归判断类型。
✅ 核心思路三步走
- 展开键值对:调用 .entries() 将顶层对象转为 [{"key":"xxx","value":"yyy"}] 数组;
- 智能值转换:对每个 value 判断是否为 JSON 对象字符串(以 { 开头),若是,则解析后提取所有 value 并拼接(.@join()),否则保留原值;
- 重构为对象:将处理后的键值对映射为独立单字段对象,再合并为最终 JSON 对象。
? 完整代码示例
import com.octomix.josson.Josson;
import com.fasterxml.jackson.databind.JsonNode;
String jsonInput = "{" +
"\"ad002\":\"5601087282462117\"," +
"\"be003\":\"{\\\"1\\\":\\\"ABC\\\",\\\"2\\\":\\\"DEF\\\",\\\"3\\\":\\\"DEF\\\"}\"," +
"\"ze024\":\"18\"" +
"}";
Josson josson = Josson.fromJsonString(jsonInput);
JsonNode result = josson.getNode(
"entries()" +
".field(value" +
".if(startsWith('{')," +
"json().entries().value.@join()," +
"value)" +
")" +
").map(key::value)" +
".mergeObjects()"
);
System.out.println(result.toPrettyString());? 输出结果
{
"ad002" : "5601087282462117",
"be003" : "ABCDEFDEF",
"ze024" : "18"
}⚠️ 注意事项
- JSON 字符串需合法转义:数据库中存储的嵌套 JSON 必须是双引号转义的字符串(如 "{\"1\":\"ABC\"}"),而非原始对象,否则 .startsWith('{') 判断会失效;
- 非对象值安全保留:数字、布尔、普通字符串等非 JSON 对象值将原样透传,避免误解析;
- 性能友好:Josson 基于 Jackson 构建,底层高效,适合批量处理千级键名;
- 调试支持:可通过 josson.getPathTrace("entries()") 分步查看中间结果,快速定位逻辑问题。
该方案彻底摆脱“写死键名”的束缚,以声明式语法实现通用 JSON 清洗,显著提升复杂 JSON 数据集成的健壮性与可维护性。










