Stream.flatMap 可将嵌套 JSON 树递归摊平为叶子节点流,核心是:值节点直接返回 Stream.of,对象/数组节点用 flatMap 递归展开;Jackson 的 JsonNode.isValueNode() 判定叶子,确保终止递归。

Stream.flatMap 可以将嵌套结构“摊平”为单层流,适用于递归遍历任意深度的 JSON 树,提取所有叶子节点(即不包含子对象或子数组的值)。
理解 flatMap 的核心作用
flatMap 接收一个函数,该函数对每个元素返回一个 Stream,然后自动将多个 Stream 合并为一个扁平的 Stream。相比 map,它天然适合处理“一对多”的嵌套展开场景。
对 JSON 而言,每个节点可能是:
- 原始值(String/Number/Boolean/Null)→ 视为叶子,直接输出
- JSONObject → 遍历其所有 value,递归处理
- JSONArray → 遍历其所有元素,递归处理
使用 Jackson + Stream 实现递归提取
假设你用 Jackson 解析 JSON 到 JsonNode(推荐,因支持动态类型判断):
关键逻辑:写一个工具方法,接收 JsonNode,返回 Stream<JsonNode>,对非叶子节点用 flatMap 递归展开,叶子节点用 Stream.of 封装。
示例代码片段:
DMap GL地图JSAPI(dmapgl)开发指南。用于编写、审查、调试 DMap 地图 API 代码,涵盖地图初始化、覆盖物渲染、图层管理、事件处理、控件交互等任务。当用户提及 dmap、dmapgl、jsapi‑gl 或相关地图开发需求时自动触发。
public static Stream<JsonNode> allLeaves(JsonNode node) {
if (node.isValueNode()) {
return Stream.of(node);
} else if (node.isObject()) {
return Stream.iterate(node.fields(), Iterator::hasNext, f -> f)
.flatMap(fields -> {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(fields, Spliterator.ORDERED),
false
).flatMap(entry -> allLeaves(entry.getValue()));
});
} else if (node.isArray()) {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(node.elements(), Spliterator.ORDERED),
false
).flatMap(element -> allLeaves(element));
}
return Stream.empty();
}
调用方式:allLeaves(rootNode).map(JsonNode::asText).collect(Collectors.toList()) 即得全部叶子值字符串列表。
注意事项与常见坑
避免无限递归:确保只对 Object 和 Array 递归,ValueNode(含 null、boolean、number、string)必须终止递归。
空值安全:JsonNode 允许字段为 null,调用 entry.getValue() 前无需额外判空,Jackson 的 null 表示为 NullNode,属于 ValueNode,会被正确捕获为叶子。
性能提示:对超大 JSON,避免反复创建 Stream;如只需统计或查找,可用 anyMatch 或 findFirst 提前终止。
替代方案对比(轻量场景可选)
若不依赖 Jackson,用 org.json 的 JSONObject/JSONArray,逻辑类似,但需手动 instanceof 判断类型,并注意 JSONArray 的迭代需用 for 循环转 Stream(IntStream.range(0, arr.length()).mapToObj(arr::get))。
若 JSON 结构固定且层级浅(如最多 3 层),也可用多重 flatMap 展开(如 .flatMap(obj -> obj.keys().asSequence().map(key -> obj.get(key)).asStream())),但不可扩展,不推荐用于“任意嵌套”场景。

















