如何从多维嵌套JSON数组中递归提取指定key的所有值
问题背景
使用Java处理多层嵌套的树形JSON结构时,初始实现仅遍历了最外层节点,仅能返回最外层的name字段值[Group],无法获取children字段下所有嵌套层级节点的目标字段值。
涉及的示例JSON结构如下:
[{"id":141741,"name":"Group","nodeTypeId":3,"deleted":false,"hasNodeAccesses":false,"children": [{"id":141742,"name":"Division","nodeTypeId":14,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141743,"name":"Site 1","nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141746,"name":"Converting","nodeTypeId":5,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141744,"name":"Site 2","nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141748,"name":"Converting","nodeTypeId":5,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141745,"name":"Site 3","nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141750,"name":"Converting","nodeTypeId":5,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141752,"name":"ML1","nodeTypeId":12,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141755,"nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141753,"name":"ML2","nodeTypeId":12,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141756,"nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[]}]},{"id":141754,"name":"ML3","nodeTypeId":12,"deleted":false,"hasNodeAccesses":false,"children":[{"id":141757,"nodeTypeId":4,"deleted":false,"hasNodeAccesses":false,"children":[]}]}]}]}]
原有问题代码如下:
public List<String> getCapexStrategyNodeNames() { JsonNode capexStrategyNodeList = client.getCapexStrategyNodes(); JSONArray nodes = capexStrategyNodeList.getArray(); List<JSONObject> nodeList = nodes.toList(); return retrieveValues(nodeList, "name"); } private List<String> retrieveValues(List<JSONObject> list, String key) { return list.stream() .map(val -> val.getString(key)) .collect(Collectors.toList()); }
问题根因:原有retrieveValues方法只做了单层列表的流处理,没有递归遍历每个节点下挂载的children子节点数组,因此无法拿到深层节点的字段值。
实现方案
核心逻辑是通过深度优先递归遍历整棵树形结构:每处理一个节点时,先提取当前节点的目标字段值,再判断当前节点是否存在非空的children数组,若存在则递归处理所有子节点,将所有层级的目标值收集到同一个结果列表中。
修改后的可运行代码如下:
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import java.util.ArrayList; import java.util.List; import java.util.Objects; public List<String> getCapexStrategyNodeNames() { JsonNode capexStrategyNodeList = client.getCapexStrategyNodes(); JSONArray nodes = capexStrategyNodeList.getArray(); List<JSONObject> rootNodeList = nodes.toJavaList(JSONObject.class); List<String> nameResult = new ArrayList<>(); traverseAndCollect(rootNodeList, "name", nameResult); return nameResult; } /** * 递归遍历所有嵌套节点,收集指定key的字段值 * @param currentLevelNodes 当前遍历层级的节点列表 * @param targetKey 需要提取的字段名 * @param collector 存储结果的列表 */ private void traverseAndCollect(List<JSONObject> currentLevelNodes, String targetKey, List<String> collector) { for (JSONObject node : currentLevelNodes) { // 提取当前节点的目标字段,做非空判断避免空指针(示例中存在无name字段的节点) String currentValue = node.getString(targetKey); if (Objects.nonNull(currentValue)) { collector.add(currentValue); } // 获取当前节点的子节点数组 JSONArray childArray = node.getJSONArray("children"); if (Objects.nonNull(childArray) && !childArray.isEmpty()) { List<JSONObject> childNodes = childArray.toJavaList(JSONObject.class); // 递归处理子层级节点 traverseAndCollect(childNodes, targetKey, collector); } } }
结果说明
针对给出的示例JSON,代码运行后返回的结果为:
[Group, Division, Site 1, Converting, Site 2, Converting, Site 3, Converting, ML1, ML2, ML3]
适配提示
- 上述代码基于FastJSON的API编写,如果使用Jackson、Gson等其他JSON处理库,仅需要替换节点取值、数组转List的对应API即可,递归遍历的核心逻辑无需改动
- 如果树形结构的子节点字段名不是
children,只需要修改代码中获取子数组的字段名即可复用逻辑 - 非空判断逻辑可根据业务需求调整,如果业务要求所有节点必须携带目标字段,可以移除非空判断,改为字段缺失时抛出对应异常
内容的提问来源于stack exchange,提问作者Herker
相关产品推荐
相关产品推荐

