You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Josson库递归计算动态JSON表达式值的技术求助

基于Josson的动态JSON表达式递归计算方案

针对你用Josson处理带表达式依赖的动态JSON需求,这里提供一套递归遍历+依赖排序+表达式计算的可行方案,完全适配未知结构的JSON:

核心思路

  1. 识别表达式节点:递归遍历整个JSON,标记所有包含expression字段的节点,记录表达式内容和节点路径。
  2. 解析依赖关系:从表达式中提取引用的字段,构建依赖图谱。
  3. 拓扑排序:按照依赖顺序排序节点,确保被依赖的字段先计算,避免语法错误。
  4. 批量计算替换:用Josson的表达式计算能力依次求值,将结果替换原expression节点。

具体实现(Java示例)

源JSON示例

{
  "A": 10,
  "B": {"expression": "A * 2"},
  "C": {"expression": "B + 5"},
  "D": {
    "sub": {"expression": "C / 3"},
    "normal": "plain text"
  },
  "list": [
    {"value": {"expression": "A + 1"}},
    {"value": 20}
  ]
}

处理代码

import com.octomix.josson.Josson;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JsonExprEvaluator {
    private static final Pattern DEP_PATTERN = Pattern.compile("([A-Za-z_][A-Za-z0-9_]*)");
    private final Josson josson;
    private final Map<String, Object> jsonData;

    public JsonExprEvaluator(Map<String, Object> jsonData) {
        this.jsonData = jsonData;
        this.josson = Josson.fromJson(jsonData);
    }

    // 递归收集所有表达式节点、路径和依赖
    private void collectExprs(Object node, String path, Map<String, String> exprMap, Map<String, Set<String>> depMap) {
        if (node instanceof Map) {
            Map<String, Object> mapNode = (Map<String, Object>) node;
            if (mapNode.containsKey("expression")) {
                String expr = mapNode.get("expression").toString();
                exprMap.put(path, expr);
                // 提取表达式中的依赖字段
                Set<String> deps = new HashSet<>();
                Matcher matcher = DEP_PATTERN.matcher(expr);
                while (matcher.find()) {
                    deps.add(matcher.group(1));
                }
                depMap.put(path, deps);
            } else {
                for (Map.Entry<String, Object> entry : mapNode.entrySet()) {
                    String newPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
                    collectExprs(entry.getValue(), newPath, exprMap, depMap);
                }
            }
        } else if (node instanceof List) {
            List<Object> listNode = (List<Object>) node;
            for (int i = 0; i < listNode.size(); i++) {
                String newPath = path + "[" + i + "]";
                collectExprs(listNode.get(i), newPath, exprMap, depMap);
            }
        }
    }

    // 拓扑排序处理依赖,确保计算顺序正确
    private List<String> topoSort(Map<String, Set<String>> depMap) {
        Map<String, Integer> inDegree = new HashMap<>();
        Queue<String> queue = new LinkedList<>();
        List<String> sortedPaths = new ArrayList<>();

        // 初始化每个节点的入度
        depMap.keySet().forEach(key -> inDegree.put(key, 0));
        depMap.values().forEach(deps -> deps.forEach(dep -> inDegree.put(dep, inDegree.getOrDefault(dep, 0) + 1)));

        // 入度为0的节点优先入队
        inDegree.entrySet().stream()
                .filter(entry -> entry.getValue() == 0)
                .map(Map.Entry::getKey)
                .forEach(queue::add);

        // 依次处理节点
        while (!queue.isEmpty()) {
            String current = queue.poll();
            sortedPaths.add(current);
            depMap.entrySet().stream()
                    .filter(entry -> entry.getValue().contains(current))
                    .forEach(entry -> {
                        int newDegree = inDegree.get(entry.getKey()) - 1;
                        inDegree.put(entry.getKey(), newDegree);
                        if (newDegree == 0) {
                            queue.add(entry.getKey());
                        }
                    });
        }

        // 检查循环依赖
        if (sortedPaths.size() != depMap.size()) {
            throw new RuntimeException("JSON中存在循环依赖,无法完成计算");
        }
        return sortedPaths;
    }

    // 计算所有表达式并替换原节点
    public void evaluateAll() {
        Map<String, String> exprMap = new HashMap<>();
        Map<String, Set<String>> depMap = new HashMap<>();
        collectExprs(jsonData, "", exprMap, depMap);

        List<String> sortedPaths = topoSort(depMap);

        for (String path : sortedPaths) {
            String expr = exprMap.get(path);
            // 用Josson计算表达式
            Object result = josson.evaluate(expr);
            // 根据路径替换原expression节点为计算结果
            replaceNodeByPath(jsonData, path, result);
        }
    }

    // 根据路径定位并替换节点
    private void replaceNodeByPath(Map<String, Object> root, String path, Object value) {
        String[] parts = path.split("\\.");
        Map<String, Object> current = root;

        for (int i = 0; i < parts.length - 1; i++) {
            String part = parts[i];
            if (part.contains("[")) {
                // 处理数组元素
                String key = part.substring(0, part.indexOf("["));
                int idx = Integer.parseInt(part.substring(part.indexOf("[") + 1, part.indexOf("]")));
                current = (Map<String, Object>) ((List<Object>) current.get(key)).get(idx);
            } else {
                current = (Map<String, Object>) current.get(part);
            }
        }

        String lastPart = parts[parts.length - 1];
        if (lastPart.contains("[")) {
            // 替换数组中的表达式节点
            String key = lastPart.substring(0, lastPart.indexOf("["));
            int idx = Integer.parseInt(lastPart.substring(lastPart.indexOf("[") + 1, lastPart.indexOf("]")));
            List<Object> list = (List<Object>) current.get(key);
            // 移除expression字段,替换为计算结果(根据你的结构调整,这里假设替换整个value节点)
            Map<String, Object> item = (Map<String, Object>) list.get(idx);
            item.put("value", value);
            item.remove("expression");
        } else {
            // 替换对象中的表达式节点
            current.put(lastPart, value);
            current.remove("expression");
        }
    }

    public static void main(String[] args) {
        String jsonStr = "{\n" +
                "  \"A\": 10,\n" +
                "  \"B\": {\"expression\": \"A * 2\"},\n" +
                "  \"C\": {\"expression\": \"B + 5\"},\n" +
                "  \"D\": {\n" +
                "    \"sub\": {\"expression\": \"C / 3\"},\n" +
                "    \"normal\": \"plain text\"\n" +
                "  },\n" +
                "  \"list\": [\n" +
                "    {\"value\": {\"expression\": \"A + 1\"}},\n" +
                "    {\"value\": 20}\n" +
                "  ]\n" +
                "}";
        Map<String, Object> jsonData = Josson.fromJson(jsonStr).getMap();
        JsonExprEvaluator evaluator = new JsonExprEvaluator(jsonData);
        evaluator.evaluateAll();
        System.out.println(Josson.fromJson(jsonData).prettyPrint());
    }
}

关键说明

  • 依赖解析:示例用正则提取简单字段名,若你的表达式包含嵌套路径(如D.sub),可调整正则或用Josson的语法分析工具优化。
  • 拓扑排序:彻底解决依赖顺序问题,避免先计算未就绪的字段导致的错误。
  • 递归遍历:自动处理嵌套对象和数组,完全适配动态JSON结构。
  • Josson优势:用Josson内置的表达式计算替代原生eval,更安全且原生支持JSON字段引用。

内容的提问来源于stack exchange,提问作者Pranav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 03:12:03