Java中未知结构JSON的通用表达式求值方案(支持calc/concat)
在Java中实现未知结构JSON的表达式求值通用方案
需求说明
需要处理任意层级的JSON结构,自动识别并求值其中的calc()、concat()这类表达式,同时严格保留原JSON的层级结构。已知固定JSON结构时可实现,但需要一套通用的递归遍历处理方案。
实现思路
核心是递归遍历JSON节点,针对不同类型节点做差异化处理:
- 对于JSON对象:遍历所有键值对,递归处理每个值,再构建新的对象节点
- 对于JSON数组:遍历所有元素,递归处理每个元素,再构建新的数组节点
- 对于字符串值:判断是否为目标表达式(如
calc(...)),解析表达式后从当前上下文(当前层级的JSON对象)提取变量值,执行计算并替换原字符串
代码实现(基于Jackson库)
首先引入Jackson依赖:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> </dependency>
通用处理工具类:
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import java.util.regex.Matcher; import java.util.regex.Pattern; public class JsonExpressionEvaluator { private static final ObjectMapper mapper = new ObjectMapper(); private static final ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); private static final Pattern CALC_PATTERN = Pattern.compile("^calc\\((.*)\\)$"); private static final Pattern CONCAT_PATTERN = Pattern.compile("^concat\\((.*)\\)$"); // 对外暴露的处理入口 public static JsonNode evaluate(JsonNode root) { return processNode(root, null); } // 递归处理节点,parentNode传递当前层级的变量上下文 private static JsonNode processNode(JsonNode node, JsonNode parentNode) { if (node.isObject()) { ObjectNode newObject = mapper.createObjectNode(); node.fields().forEachRemaining(entry -> { String key = entry.getKey(); JsonNode value = entry.getValue(); newObject.set(key, processNode(value, node)); }); return newObject; } else if (node.isArray()) { ArrayNode newArray = mapper.createArrayNode(); node.elements().forEachRemaining(element -> { newArray.add(processNode(element, parentNode)); }); return newArray; } else if (node.isTextual()) { String text = node.asText(); // 处理calc算术表达式 Matcher calcMatcher = CALC_PATTERN.matcher(text); if (calcMatcher.matches() && parentNode != null) { String expr = calcMatcher.group(1); String replacedExpr = replaceVariables(expr, parentNode); try { Object result = engine.eval(replacedExpr); return mapper.valueToTree(result); } catch (Exception e) { // 表达式解析失败则保留原字符串 return node; } } // 处理concat字符串拼接表达式 Matcher concatMatcher = CONCAT_PATTERN.matcher(text); if (concatMatcher.matches() && parentNode != null) { String parts = concatMatcher.group(1); String[] partArr = parts.split(","); StringBuilder sb = new StringBuilder(); for (String part : partArr) { String trimmedPart = part.trim(); if (parentNode.has(trimmedPart)) { sb.append(parentNode.get(trimmedPart).asText()); } else { sb.append(trimmedPart.replace("'", "").replace("\"", "")); } } return mapper.valueToTree(sb.toString()); } // 非表达式字符串直接返回 return node; } else { // 数值、布尔等非字符串类型直接返回 return node; } } // 替换表达式中的变量为当前上下文的实际值 private static String replaceVariables(String expr, JsonNode parentNode) { Pattern varPattern = Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*)"); Matcher varMatcher = varPattern.matcher(expr); StringBuffer sb = new StringBuffer(); while (varMatcher.find()) { String varName = varMatcher.group(1); if (parentNode.has(varName)) { JsonNode varValue = parentNode.get(varName); if (varValue.isNumber()) { varMatcher.appendReplacement(sb, varValue.asText()); } else if (varValue.isTextual()) { varMatcher.appendReplacement(sb, "'" + varValue.asText() + "'"); } else { varMatcher.appendReplacement(sb, varName); } } else { varMatcher.appendReplacement(sb, varName); } } varMatcher.appendTail(sb); return sb.toString(); } // 测试示例 public static void main(String[] args) throws Exception { String sourceJson = """ { "a": 100, "b": 200, "c": 300, "d": "calc(a+c)", "f": { "g": 100, "h": 200, "i": "calc(g+h)", "k": { "m": 50, "n": 70, "p": "calc(m+n)" } } } """; JsonNode root = mapper.readTree(sourceJson); JsonNode result = evaluate(root); System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(result)); } }
关键说明
- 递归遍历:通过
processNode方法递归处理所有类型的JSON节点,确保任意嵌套层级都能被覆盖 - 表达式识别:用正则匹配目标表达式格式,后续扩展新表达式类型只需添加对应正则和处理逻辑
- 上下文变量替换:从当前层级的JSON对象中提取变量值,替换表达式中的变量后执行计算
- ScriptEngine:借助Java内置的JavaScript引擎执行表达式,支持常见算术运算,无需手动实现复杂的表达式解析
- 容错处理:表达式解析失败时保留原字符串,避免破坏JSON原有结构
扩展方向
- 支持跨层级变量引用:修改上下文传递逻辑,实现
f.g这类层级变量的引用 - 增加聚合表达式:比如
sum()、average(),扩展对应的处理逻辑即可 - 安全增强:若处理不可信来源的JSON,需限制ScriptEngine权限,或使用更安全的表达式解析库(如JEXL)
内容的提问来源于stack exchange,提问作者dattam katore
相关产品推荐
相关产品推荐

