
本文详解如何基于 Josson 库(集成 mXparser)对结构未知的 JSON 对象进行递归遍历与表达式求值,支持跨字段依赖(如 D 引用 C 的计算结果),并提供可自动构建转换表达式的通用方案。
本文详解如何基于 josson 库(集成 mxparser)对结构未知的 json 对象进行递归遍历与表达式求值,支持跨字段依赖(如 `d` 引用 `c` 的计算结果),并提供可自动构建转换表达式的通用方案。
在实际工程中,常需处理“配置即代码”型 JSON:部分字段为原始值(如数字、字符串),部分则为动态表达式(如 "calc(A+B,A:data.A,B:data.B)"),且表达式间存在依赖关系(D 依赖 C 的计算结果)。Josson 是一款功能强大的 Java JSON 查询与转换库,其 eval() 函数结合底层 mXparser 数学引擎,天然支持此类场景——但直接硬编码路径易出错、不可扩展,且需规避语法陷阱(如保留字冲突)与非法求值(如对 PDF 文件名执行 calc)。
✅ 核心原则:结构化表达式 + 按需求值
为安全、清晰地分离数据与逻辑,推荐采用 显式表达式对象 结构(如 "C": {"expression": "calc(A+B)"}),而非将表达式字符串直接作为字段值。这不仅避免了 C(mXparser 中为组合数函数)等保留字引发的 Calc syntax error,也使解析逻辑更鲁棒。
以下为标准处理流程:
1. 构建 Josson 实例并加载 JSON
String json = "{\n" +
" \"data\": {\n" +
" \"A\": 1688,\n" +
" \"B\": 1363,\n" +
" \"c\": {\"expression\": \"calc(A+B)\"},\n" +
" \"D\": {\"expression\": \"calc(B+c)\"},\n" +
" \"drg\": \"TEMPLATE(12).pdf\"\n" +
" },\n" +
" \"tbl02Modelmaster\": {\n" +
" \"A\": {\"expression\": \"calc(A+100,A:$.data.A)\"},\n" +
" \"stageType\": \"Multi\",\n" +
" \"isPowerConstraint\": true\n" +
" }\n" +
"}";
Josson josson = Josson.fromJsonString(json);⚠️ 注意:字段名改用小写 c 替代 C,彻底规避 mXparser 保留字冲突。
2. 手动编写转换表达式(适用于已知结构)
利用 Josson 的链式 field() 与 eval() 实现按依赖顺序求值:
JsonNode result = josson.getNode(
"field(data" +
".field(c: eval(c.expression))" + // 先算 c
".field(D: eval(D.expression))" + // 再算 D(可引用 c)
")" +
".field(tbl02Modelmaster" +
".field(A: eval(A.expression))" + // 独立计算 A
")"
);
System.out.println(result.toPrettyString());输出中 c 和 D 已被替换为数值(3051.0, 4414.0),其他原始值保持不变。
3. 自动构建表达式(推荐:适配任意动态结构)
面对未知嵌套层级与字段名,需程序化生成 Josson 路径表达式。关键思路:递归扫描所有对象,识别含 expression 字段的节点,并为其生成 .field(KEY: eval(KEY.expression)) 子句。
// 自动提取所有需求值的字段路径
String transformExpr = josson.getString(
"entries()" + // 遍历根级键值对
".concat('field('," +
"key," + // 当前对象名(如 "data")
"value.entries()" + // 进入该对象内部
".[value.expression != null]*" + // 筛选 value 为 {expression: "..."} 的项
".concat('.field(', key, ': eval(', key, '.expression))')" + // 生成求值子句
".join()," + // 合并所有子句
"')" + // 闭合 field()
".join('.')" // 合并各顶层对象的 field()
);
System.out.println("Auto-generated expression: " + transformExpr);
// 输出: field(data.field(c: eval(c.expression)).field(D: eval(D.expression))).field(tbl02Modelmaster.field(A: eval(A.expression)))
// 执行动态表达式
JsonNode finalResult = josson.getNode(transformExpr);此方法完全解耦业务逻辑与 JSON 结构,新增一个带 expression 的字段(如 "E": {"expression": "calc(c*D)"})后,无需修改 Java 代码,仅需更新 JSON 即可生效。
? 关键注意事项
- 依赖顺序:Josson 按表达式书写顺序执行 eval,确保被依赖字段(如 c)的 field() 出现在依赖字段(如 D)之前。
- 作用域规则:eval() 默认在当前对象上下文中执行。若需跨对象引用(如 tbl02Modelmaster.A 引用 data.A),必须显式指定路径(如 A: $.data.A),这已在示例中体现。
- 错误防御:eval() 遇到非法表达式会抛异常。生产环境建议包裹 try-catch 并记录原始表达式,便于排查。
- 性能考量:对超大 JSON,频繁调用 getNode() 可能有开销。若需多次变换,可预先编译表达式(Josson 支持 Josson.compile())。
通过上述方案,你已掌握一种兼具健壮性、扩展性与专业性的 JSON 表达式求值范式——它不再受限于固定 schema,而是让 JSON 本身成为可执行的配置脚本。










