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

Java从可变深度JSON中提取各子树ID列表的实现方案

基于Jackson提取JSON树的ID路径列表

递归方案(修改原有遍历方法)

假设你的JSON节点结构包含id字段和children子节点数组,只需在原有递归遍历逻辑中加入路径回溯的逻辑,即可收集所有根到节点的路径(支持叶子节点或所有节点)。

提取根到叶子节点的路径

如果只需要叶子节点的完整路径(如示例中的1,3,6),修改后的递归代码如下:

import com.fasterxml.jackson.databind.JsonNode;
import java.util.ArrayList;
import java.util.List;

public class JsonTreePathExtractor {

    public List<List<Integer>> getLeafIdPaths(JsonNode rootNode) {
        List<List<Integer>> paths = new ArrayList<>();
        if (rootNode == null || !rootNode.has("id")) return paths;
        traverseLeafNodes(rootNode, new ArrayList<>(), paths);
        return paths;
    }

    private void traverseLeafNodes(JsonNode currentNode, List<Integer> currentPath, List<List<Integer>> result) {
        // 将当前节点ID加入路径
        int currentId = currentNode.get("id").asInt();
        currentPath.add(currentId);

        JsonNode children = currentNode.get("children");
        // 判断是否为叶子节点(无有效子节点)
        if (children == null || !children.isArray() || children.size() == 0) {
            // 保存路径副本,避免后续修改影响结果
            result.add(new ArrayList<>(currentPath));
        } else {
            // 递归遍历所有子节点
            for (JsonNode child : children) {
                traverseLeafNodes(child, currentPath, result);
            }
        }

        // 回溯:移除当前节点ID,保证递归返回后路径状态正确
        currentPath.remove(currentPath.size() - 1);
    }
}

提取所有节点的完整路径

如果需要收集每一个节点的根到自身的路径(比如根节点1、子节点1,3等),只需调整逻辑,在加入当前节点ID后立即保存路径:

private void traverseAllNodes(JsonNode currentNode, List<Integer> currentPath, List<List<Integer>> result) {
    int currentId = currentNode.get("id").asInt();
    currentPath.add(currentId);
    // 保存当前节点的路径
    result.add(new ArrayList<>(currentPath));

    JsonNode children = currentNode.get("children");
    if (children != null && children.isArray() && children.size() > 0) {
        for (JsonNode child : children) {
            traverseAllNodes(child, currentPath, result);
        }
    }

    currentPath.remove(currentPath.size() - 1);
}

非递归方案(避免深度过大栈溢出)

如果你的JSON树深度极大,递归可能导致栈溢出,这时可以用栈模拟递归过程,通过创建路径副本来替代回溯:

import com.fasterxml.jackson.databind.JsonNode;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class JsonTreePathExtractor {

    public List<List<Integer>> getLeafIdPathsNonRecursive(JsonNode rootNode) {
        List<List<Integer>> paths = new ArrayList<>();
        if (rootNode == null || !rootNode.has("id")) return paths;

        // 栈中存储(当前节点,当前路径)
        Stack<Object[]> nodeStack = new Stack<>();
        nodeStack.push(new Object[]{rootNode, new ArrayList<Integer>()});

        while (!nodeStack.isEmpty()) {
            Object[] entry = nodeStack.pop();
            JsonNode currentNode = (JsonNode) entry[0];
            List<Integer> parentPath = (List<Integer>) entry[1];

            // 创建当前节点的路径副本
            List<Integer> currentPath = new ArrayList<>(parentPath);
            currentPath.add(currentNode.get("id").asInt());

            JsonNode children = currentNode.get("children");
            if (children == null || !children.isArray() || children.size() == 0) {
                paths.add(currentPath);
            } else {
                // 栈是后进先出,倒序压入子节点保证遍历顺序和递归一致
                for (int i = children.size() - 1; i >= 0; i--) {
                    nodeStack.push(new Object[]{children.get(i), currentPath});
                }
            }
        }
        return paths;
    }
}

适配调整说明

  1. ID类型适配:如果ID是字符串而非整数,将asInt()替换为asText(),路径列表改为List<String>即可。
  2. 子节点字段名适配:如果子节点字段不是children,修改代码中currentNode.get("children")的字段名(比如subNodes)。
  3. 路径格式转换:若需要将路径转为逗号分隔的字符串(如"1,3,6"),可以用以下方式转换:
    List<String> stringPaths = paths.stream()
        .map(path -> String.join(",", path.stream().map(String::valueOf).toArray(String[]::new)))
        .toList();
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 12:07:45