
本文介绍一种无需硬编码键名的通用方案,使用 josson 库遍历任意结构的 json 对象,对值为 json 字符串的字段自动反序列化并拼接其所有内部值,最终生成标准化的扁平化 json 结果。
在实际开发中,数据库常以字符串形式存储动态 JSON 数据(如 {"be003": "{\"1\":\"ABC\",\"2\":\"DEF\",\"3\":\"DEF\"}"}),且顶层键名数量庞大、不可预知。若采用传统 JSONObject.optString("key") 方式逐个提取,不仅代码冗余,更难以维护和扩展。
推荐使用轻量级 JSON 转换库 Josson ——它支持声明式路径表达式,可高效完成“无键遍历 + 条件解析 + 值聚合”三步操作。
✅ 核心处理逻辑(分步解析)
- entries():将顶层对象转为键值对数组 [{"key":"ad002","value":"..."}, ...];
-
.field(value.if(startsWith('{'), json().entries().value.@join(), ?)):
- 判断 value 是否以 { 开头(即是否为 JSON 对象字符串);
- 若是,则解析该字符串为 JSON → 提取所有 entries() → 获取每个子项的 value → 用 @join() 拼接为单个字符串;
- 否则保留原值(如 "5601087282462117");
- .map(key::value) + .mergeObjects():将键值对数组还原为标准 JSON 对象。
? 完整示例代码
import com.octomix.josson.Josson;
import com.fasterxml.jackson.databind.JsonNode;
public class JsonFlattener {
public static void main(String[] args) {
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()," +
" ?)" +
" )" +
".map(key::value)" +
".mergeObjects()"
);
System.out.println(result.toPrettyString());
// 输出:
// {
// "ad002" : "5601087282462117",
// "be003" : "ABCDEFDEF",
// "ze024" : "18"
// }
}
}⚠️ 注意事项
- 确保嵌套 JSON 字符串严格符合 JSON 语法(双引号、正确转义),否则 json() 函数会抛出解析异常;
- @join() 默认以空字符串拼接,如需分隔符(如逗号),可改用 @join(', ');
- 若存在多层嵌套(如 be003 的值本身还包含 JSON 字符串),需递归应用逻辑,可通过自定义函数或多次 .getNode() 实现;
- 引入依赖(Maven):
com.octomix.josson josson 1.7.1
该方案彻底摆脱了对键名的依赖,适用于日志分析、配置中心、动态表单等场景,兼具灵活性与可读性。










