在 Java 中遍历 JSON 可借助第三方库(如 Jackson、Gson、org.json),先将 JSON 字符串解析为
JsonNode/
JsonObject(Jackson)或
JsonElement(Gson),再通过递归或迭代方式遍历键
前置准备:选择适合的JSON处理库
Java生态中有多个成熟的JSON处理库,最常用的三者及其特点如下表所示:
| 库名称 | 特点 | 适用场景 |
|---|---|---|
| Jackson | 高性能、功能全面(支持注解绑定/流式API)、广泛集成于Spring框架 | 企业级应用、复杂对象映射 |
| Gson | 轻量级、语法简洁、对泛型支持友好 | 快速开发、移动端应用 |
| org.json | 纯Java实现、无依赖、API简单直观 | 小型项目、教学演示 |
推荐优先学习Jackson(工业标准)和Gson(简洁高效),二者合计可覆盖90%以上的业务需求。
基于Jackson库的深度遍历方案
环境配置
Maven依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
核心实现步骤
步骤①:将JSON字符串转换为JsonNode对象JsonNode是Jackson提供的树形结构节点,可灵活访问任意层级的数据。
步骤②:递归遍历节点类型
根据节点类型(对象/数组/值)进行差异化处理:
OBJECT→ 遍历所有字段名+子节点ARRAY→ 遍历数组元素VALUE→ 获取具体值(字符串/数字/布尔值等)
完整代码示例:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
public class JacksonTraversal {
private static void traverse(JsonNode node, String path) {
if (node.isObject()) { // 处理JSON对象
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
String newPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
traverse(entry.getValue(), newPath);
}
} else if (node.isArray()) { // 处理JSON数组
for (int i = 0; i < node.size(); i++) {
String newPath = path + "[" + i + "]";
traverse(node.get(i), newPath);
}
} else { // 基本类型值(终端节点)
System.out.println("Path: " + path + " | Value: " + node.toString());
}
}
public static void main(String[] args) throws IOException {
String jsonStr = "{"name":"张三","age":30,"hobbies":["游泳","阅读"],"address":{"city":"北京","street":"长安街"}}";
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonStr);
traverse(rootNode, "");
}
}
输出结果:
Path: name | Value: "张三"
Path: age | Value: 30
Path: hobbies[0] | Value: "游泳"
Path: hobbies[1] | Value: "阅读"
Path: address.city | Value: "北京"
Path: address.street | Value: "长安街"
关键技巧:
- 路径拼接:通过
path参数记录当前节点的访问路径,便于定位数据位置 - 类型判断:必须严格区分
isObject()和isArray(),否则会抛出异常 - 空值处理:可通过
node.isNull()判断空节点,避免NPE
基于Gson库的扁平化遍历方案
环境配置
Maven依赖:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
核心实现思路
Gson默认将JSON转换为Map<String, Object>或List<Object>,适合逐层展开的扁平化遍历。
代码示例:
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.util.Stack;
public class GsonTraversal {
public static void traverse(JsonElement element, String currentPath) {
if (element.isJsonObject()) { // 处理JSON对象
JsonObject obj = element.getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
String newPath = currentPath.isEmpty() ? entry.getKey() : currentPath + "." + entry.getKey();
traverse(entry.getValue(), newPath);
}
} else if (element.isJsonArray()) { // 处理JSON数组
JsonArray arr = element.getAsJsonArray();
for (int i = 0; i < arr.size(); i++) {
String newPath = currentPath + "[" + i + "]";
traverse(arr.get(i), newPath);
}
} else { // 基本类型值
System.out.println("Path: " + currentPath + " | Value: " + element.toString());
}
}
public static void main(String[] args) {
String jsonStr = "{"name":"李四","scores":[95,88,76],"info":{"gender":"男","height":175}}";
Gson gson = new Gson();
JsonElement root = JsonParser.parseString(jsonStr);
traverse(root, "");
}
}
输出结果:
Path: name | Value: "李四"
Path: scores[0] | Value: 95
Path: scores[1] | Value: 88
Path: scores[2] | Value: 76
Path: info.gender | Value: "男"
Path: info.height | Value: 175
优势对比:
- Gson的API更简洁,无需显式创建
ObjectMapper - 对原始类型(如整数、浮点数)的解析更直接
- 适合需要快速验证数据结构的调试场景
特殊场景处理指南
动态键名的遍历(未知结构)
当JSON结构不固定时,需采用通用方式处理:
// Jackson实现
JsonNode dynamicNode = rootNode.get("dynamicKey");
if (dynamicNode != null && !dynamicNode.isMissingNode()) {
// 继续遍历...
}
// Gson实现
if (root.has("dynamicKey")) {
JsonElement ele = root.get("dynamicKey");
// 处理逻辑...
}
大数据量的内存优化
对于超过1MB的JSON数据,建议改用流式API(仅Jackson支持):
ObjectMapper mapper = new ObjectMapper();
JsonParser parser = mapper.getFactory().createParser(new File("large.json"));
while (parser.nextToken() != null) {
// 按需处理每个token,避免全量加载到内存
}
复杂类型转换(如日期/枚举)
需配合自定义反序列化器:
// Jackson示例:注册模块处理日期格式
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Jdk8Module()); // Java8时间类支持
mapper.setDateFormat(df);
常见错误及解决方案
| 错误类型 | 原因分析 | 解决方法 |
|---|---|---|
JsonParseException |
JSON格式非规(如逗号缺失) | 使用在线工具校验JSON格式 |
ClassCastException |
尝试将数组强制转为对象 | 遍历前检查isArray()/isObject() |
UnrecognizedPropertyException |
JSON包含未定义的字段 | 配置@JsonIgnoreProperties注解 |
OutOfMemoryError |
单次加载过大JSON文件 | 改用流式解析或分割文件 |
相关问答FAQs
Q1: 如何从嵌套JSON中提取特定路径的值?
答:以Jackson为例,可通过链式调用精确定位:
JsonNode cityNode = rootNode.path("address").path("city").get(); // 安全获取方式(不存在返回missing node)
String city = cityNode.asText(); // 转换为字符串
若使用Gson,需分段解析:
JsonObject addressObj = root.getAsJsonObject("address");
String city = addressObj.get("city").getAsString();
Q2: 遍历时遇到大量重复键怎么办?
答:JSON规范允许对象内有重复键,但后续值会覆盖前者,若需保留所有值,应改用Multimap结构存储:
// Jackson解决方案
ObjectMapper mapper = new ObjectMapper();
List<JsonNode> values = mapper.readValue(jsonStr, new TypeReference<List<JsonNode>>(){});
// 后续按索引访问即可避免覆盖问题
