Java案例怎么解析JSON数据?从入门到实战的完整指南
目录导读
- JSON数据与Java解析概述
- 主流Java JSON解析库对比
- 实战案例:使用Jackson解析JSON数据
- 实战案例:使用Gson解析JSON数据
- 复杂JSON结构解析技巧
- JSON解析常见问题与性能优化
- 扩展阅读:JSON Schema验证与Spring Boot集成
JSON数据与Java解析概述
JSON(JavaScript Object Notation)已成为Web API和现代应用中最主流的数据交换格式,在Java开发中,解析JSON数据是每个开发者必须掌握的技能,无论你是处理REST API响应、读取配置文件还是对接第三方服务,理解如何高效解析JSON都至关重要。

为什么需要解析JSON?
Java是强类型语言,而JSON是轻量级文本格式,解析过程本质上是将JSON字符串映射为Java对象,或将Java对象转换为JSON字符串(序列化与反序列化)。
核心概念
- 序列化:Java对象 → JSON字符串
- 反序列化:JSON字符串 → Java对象
- 树模型:将JSON解析为树形结构(如JsonNode)
- 流式解析:逐令牌处理,适合超大JSON
常见应用场景
- 微服务间通信
- 移动端与后端数据交互
- 日志文件分析
- 配置管理
主流Java JSON解析库对比
在选择解析库时,需要综合考虑性能、易用性、社区活跃度,以下是三大主流库的详细对比:
| 特性 | Jackson | Gson | Fastjson |
|---|---|---|---|
| 性能排名 | 第一梯队 | 第二梯队 | 第一梯队(但存在安全争议) |
| 学习曲线 | 中等 | 低 | 低 |
| 自定义能力 | 极强(通过注解和模块) | 中等(通过TypeAdapter) | 中等 |
| 增强功能 | YAML、XML、CSV支持 | 无 | 无 |
| 社区活跃度 | 最高(Spring Boot默认) | 高(Google出品) | 中等(阿里出品) |
| 安全漏洞 | 低 | 低 | 曾出现高危漏洞 |
推荐选择标准:
- 新项目优先选择Jackson(Spring Boot生态支持)
- 简单项目或老系统维护可继续使用Gson
- Fastjson建议仅用于已知安全加固的旧项目
实战案例:使用Jackson解析JSON数据
环境准备
在pom.xml中添加依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
案例1:解析简单JSON对象
假设有以下JSON:
{"name":"张三","age":28,"email":"zhangsan@example.com"}
Java实现:
import com.fasterxml.jackson.databind.ObjectMapper;
public class SimpleParse {
public static void main(String[] args) throws Exception {
String json = "{\"name\":\"张三\",\"age\":28,\"email\":\"zhangsan@example.com\"}";
ObjectMapper mapper = new ObjectMapper();
// 方式一:直接反序列化为Map
Map<String, Object> map = mapper.readValue(json, Map.class);
System.out.println("姓名: " + map.get("name"));
// 方式二:反序列化为自定义对象
User user = mapper.readValue(json, User.class);
System.out.println("年龄: " + user.getAge());
}
}
User类定义:
public class User {
private String name;
private int age;
private String email;
// getters/setters 省略(必须提供)
}
案例2:解析JSON数组
[{"city":"北京","temp":30},{"city":"上海","temp":35}]
String jsonArrayStr = "[{\"city\":\"北京\",\"temp\":30},{\"city\":\"上海\",\"temp\":35}]";
List<Weather> weatherList = mapper.readValue(jsonArrayStr,
new TypeReference<List<Weather>>(){});
核心技巧
- 使用@JsonProperty注解处理字段名不一致问题
- 对于未知字段使用@JsonIgnoreProperties(ignoreUnknown = true)
- 处理空对象使用@JsonInclude(Include.NON_NULL)
- 处理日期格式使用@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
实战案例:使用Gson解析JSON数据
添加依赖:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
案例:解析嵌套JSON
{
"code": 200,
"message": "success",
"data": {
"list": [
{"id": 1, "name": "产品A", "price": 99.9}
],
"total": 1
}
}
Java代码:
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class GsonNestedParse {
public static void main(String[] args) {
String complexJson = "..."; // 上述JSON字符串
Gson gson = new Gson();
// 解析为Map
Map<String, Object> result = gson.fromJson(complexJson, Map.class);
Map<String, Object> data = (Map<String, Object>) result.get("data");
List<Map<String, Object>> list = (List<Map<String, Object>>) data.get("list");
// 推荐方式:解析为泛型对象
ApiResponse<DataResponse> response = gson.fromJson(complexJson,
new TypeToken<ApiResponse<DataResponse>>(){}.getType());
}
}
Gson特殊场景处理
- 处理null值:GsonBuilder().serializeNulls()
- 日期格式:GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss")
- 排除字段:使用@Expose注解配合GsonBuilder().excludeFieldsWithoutExposeAnnotation()
复杂JSON结构解析技巧
场景1:动态字段名
有时JSON的键名是动态生成的,如:
{"user_123":{"name":"测试"},"user_456":{"name":"示例"}}
解决方案:
Map<String, User> dynamicMap = mapper.readValue(json,
new TypeReference<Map<String, User>>(){});
场景2:深度嵌套与循环引用
使用@JsonManagedReference和@JsonBackReference处理双向关联,或启用循环引用检测:
ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.disable(SerializationFeature.FAIL_ON_SELF_REFERENCES);
场景3:超大JSON流式解析
当JSON文件超过内存限制时,使用JsonParser流式处理:
JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(new File("huge.json"));
while (parser.nextToken() != JsonToken.END_OBJECT) {
String fieldName = parser.getCurrentName();
if ("data".equals(fieldName)) {
parser.nextToken(); // 进入数组
while (parser.nextToken() != JsonToken.END_ARRAY) {
// 逐条处理
}
}
}
JSON解析常见问题与性能优化
问题1:日期格式解析失败
不同系统可能使用不同日期格式,建议统一使用ISO 8601格式,并配置:
ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
问题2:忽略null值
传输大量null字段浪费带宽,配置Jackson全局忽略null:
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
问题3:安全防范
- 禁用Jackson的enableDefaultTyping()(防止反序列化攻击)
- 使用Gson时设置GsonBuilder().disableHtmlEscaping()
- 验证JSON输入长度,避免拒绝服务攻击
性能优化建议
- 复用ObjectMapper实例(线程安全)
- 使用字节流而非字符流处理大文件
- 根据场景选择树模型或流模型
- 开启Jackson的SMALL_AS_TEXT或者FAST模式
扩展阅读:JSON Schema验证与Spring Boot集成
JSON Schema验证
使用networknt的json-schema-validator库:
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<version>1.0.74</version>
</dependency>
验证示例:
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4);
JsonSchema schema = factory.getSchema(new File("schema.json"));
ProcessingReport report = schema.validate(JsonLoader.fromString(jsonData));
Spring Boot中的JSON处理
Spring Boot自动配置了Jackson,只需在Controller中使用@RequestBody注解:
@PostMapping("/user")
public ResponseEntity<User> createUser(@RequestBody @Valid User user) {
// 参数已自动反序列化
return ResponseEntity.ok(userService.save(user));
}
配置Jackson行为
在application.yml中:
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
serialization:
write-dates-as-timestamps: false
deserialization:
fail-on-unknown-properties: false
问答环节
Q1:Jackson和Gson哪个更适合初学者?
A:Gson的学习曲线更低,API更直观,但考虑到Jackson是Spring Boot的默认解析器,建议初学者优先学习Jackson,后期迁移成本更低。
Q2:解析JSON时出现UnrecognizedPropertyException如何解决?
A:在POJO类上添加@JsonIgnoreProperties(ignoreUnknown = true)注解,或在ObjectMapper中配置mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)。
Q3:如何解析带下划线的JSON字段到驼峰命名的Java属性?
A:使用@JsonProperty("user_name")注解,或全局配置命名策略:mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)。
Q4:JSON解析性能和安全哪个更重要?
A:对于中小型应用(JSON小于10MB),性能差异可以忽略,安全性应优先考虑,建议始终禁用自动类型绑定,并限制JSON输入大小。
Q5:如何处理JSON中的超大数字导致精度丢失问题?
A:将对应字段类型改为BigDecimal,或使用Jackson的@JsonFormat(shape = JsonFormat.Shape.STRING)将数字序列化为字符串。