Java中JSON序列化与反序列化的原理与实践方法
作者:霸道流氓气质
一、什么是序列化/反序列化
1.1 基本概念
| 术语 | 含义 | 方向 | 类比 |
|---|---|---|---|
| 序列化(Serialize) | 将 Java 对象转换为字符串/字节流 | 对象 → 字符串 | 把立体模型拍成照片(方便传输) |
| 反序列化(Deserialize) | 将字符串/字节流还原为 Java 对象 | 字符串 → 对象 | 看照片还原出立体模型 |
1.2 为什么需要
Java 对象存在于 JVM 内存中,只有当前进程能访问。
要在以下场景使用,必须先转换为通用格式:
- 网络传输:前端 ↔ 后端(HTTP Body)
- 微服务调用:服务A ↔ 服务B(Feign/HTTP)
- 消息队列:生产者 → MQ → 消费者
- 缓存存储:对象 → Redis(字符串存储)
- 数据库:JSON 字段存储复杂结构
- 日志记录:对象内容输出到日志文件
1.3 JSON 格式简介
JSON(JavaScript Object Notation)是最通用的数据交换格式:
{
"orderId": 12345,
"orderCode": "xx.20260805.000001",
"amount": 3999.00,
"paid": true,
"items": [
{"skuId": 100, "name": "11", "qty": 1},
{"skuId": 200, "name": "22", "qty": 2}
],
"address": null
}JSON 数据类型: 字符串、数字、布尔、null、对象({})、数组([])
二、Java 中的 JSON 框架
| 框架 | 出品方 | 特点 |
|---|---|---|
| Jackson | FasterXML | Spring 默认集成,功能最全 |
| Fastjson | 阿里巴巴 | 性能极高,API 简洁 |
| Gson | 轻量级,容错性好 |
三、Jackson 核心原理
3.1 序列化过程(对象 → JSON)
Java 对象
│
├─ ObjectMapper 通过反射获取类的所有字段
│ ├─ 遍历 getter 方法(默认行为)
│ ├─ 或遍历字段(配置后)
│ └─ 检查注解(@JsonIgnore、@JsonProperty 等)
│
├─ 对每个字段:
│ ├─ 获取字段名 → 作为 JSON key
│ ├─ 获取字段值 → 转换为 JSON value
│ │ ├─ String → "字符串"
│ │ ├─ Integer/Long → 数字
│ │ ├─ Boolean → true/false
│ │ ├─ null → null
│ │ ├─ List/Array → [...]
│ │ ├─ 嵌套对象 → {...}(递归序列化)
│ │ └─ Date → 时间戳或格式化字符串
│ └─ 拼接为 "key": value
│
└─ 输出完整 JSON 字符串
3.2 反序列化过程(JSON → 对象)
JSON 字符串
│
├─ ObjectMapper 解析 JSON 为 Token 流
│ (类似词法分析:{ → START_OBJECT, "name" → FIELD_NAME, ...)
│
├─ 根据目标类型 Class<T>:
│ ├─ 创建目标类实例(无参构造器或 @JsonCreator)
│ ├─ 遍历 JSON 中的每个 key-value:
│ │ ├─ 根据 key 找到 Java 类中对应的 setter/字段
│ │ ├─ 将 JSON value 转换为 Java 类型
│ │ └─ 调用 setter 设置值
│ └─ 返回填充完毕的对象
│
└─ 未知字段处理:
├─ 默认:抛异常 UnrecognizedPropertyException
└─ 配置 FAIL_ON_UNKNOWN_PROPERTIES=false:忽略
四、核心注解
| 注解 | 作用 | 示例 |
|---|---|---|
@JsonProperty | 指定 JSON key 名(与 Java 字段名不同时) | @JsonProperty("order_code") |
@JsonIgnore | 序列化/反序列化时忽略该字段 | 内部计算字段不返回给前端 |
@JsonFormat | 日期格式化 | @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss") |
@JsonInclude | 控制 null 值是否序列化 | @JsonInclude(Include.NON_NULL) |
@JsonAlias | 反序列化时接受多个别名 | @JsonAlias({"name","userName"}) |
五、泛型反序列化(TypeReference)
5.1 问题:泛型擦除
// ❌ 编译期泛型信息会被擦除 List<OrderDto> list = objectMapper.readValue(json, List.class); // 实际得到的是 List<LinkedHashMap>,不是 List<OrderDto>! // 因为运行时 JVM 不知道 List 里应该是什么类型
5.2 解决方案:TypeReference
// ✅ TypeReference 保留了完整的泛型信息
List<OrderDto> list = objectMapper.readValue(json,
new TypeReference<List<OrderDto>>() {});
// 正确反序列化为 List<OrderDto>
// 嵌套泛型
RestControllerResult<List<OrderDto>> result = objectMapper.readValue(json,
new TypeReference<RestControllerResult<List<OrderDto>>>() {});
原理: TypeReference 通过匿名内部类在编译时保留了泛型参数信息(写入 class 文件的 Signature 属性中),运行时可以通过反射读取。
六、通用示例代码
6.1 Spring MVC 自动序列化(最常用)
/**
* Spring MVC 自动处理 JSON 序列化/反序列化.
* 无需手动调用 ObjectMapper.
*/
@RestController
public class OrderController {
/**
* 接收 JSON 请求体 → 自动反序列化为 Java 对象.
* 返回 Java 对象 → 自动序列化为 JSON 响应.
*/
@PostMapping("/api/order/create")
public RestControllerResult<OrderDto> createOrder(@RequestBody CreateOrderDto dto) {
// Spring 自动完成:
// 1. 请求体 JSON → CreateOrderDto(反序列化)
// 2. 返回值 RestControllerResult → JSON 响应体(序列化)
OrderDto result = orderService.create(dto);
return new RestControllerResult<>(result);
}
}
// 前端发送:
// POST /api/order/create
// Content-Type: application/json
// {"orderCode":"SO.001","amount":3999.00,"items":[{"skuId":1,"qty":2}]}
// 后端返回:
// {"success":true,"code":0,"data":{"orderId":123,"orderCode":"SO.001",...}}
6.2 手动序列化/反序列化
/**
* 手动使用 ObjectMapper 的场景:
* - 日志记录对象内容
* - 调用第三方接口组装请求体
* - Redis 缓存存取
* - MQ 消息体
*/
public class JsonDemo {
// 全局单例 ObjectMapper(线程安全,不要每次 new)
private static final ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) // 忽略未知字段
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true); // 日期转时间戳
/**
* 对象 → JSON 字符串.
*/
public static String toJson(Object obj) {
try {
return objectMapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
log.error("JSON序列化失败", e);
return "";
}
}
/**
* JSON 字符串 → 对象.
*/
public static <T> T fromJson(String json, Class<T> clazz) {
try {
return objectMapper.readValue(json, clazz);
} catch (Exception e) {
log.error("JSON反序列化失败", e);
return null;
}
}
/**
* JSON 字符串 → 泛型对象(如 List<T>、Map<K,V>).
*/
public static <T> T fromJson(String json, TypeReference<T> typeRef) {
try {
return objectMapper.readValue(json, typeRef);
} catch (Exception e) {
log.error("JSON反序列化失败", e);
return null;
}
}
}
6.3 Fastjson 用法(阿里巴巴)
/**
* Fastjson
*
*/
public class FastjsonDemo {
// 序列化
String json = JSON.toJSONString(orderDto);
// 反序列化 - 简单类型
OrderDto dto = JSON.parseObject(json, OrderDto.class);
// 反序列化 - 泛型类型
DuResultDto<List<QueryResultDto>> result = JSONObject.parseObject(
responseStr,
new TypeReference<DuResultDto<List<QueryResultDto>>>() {});
// 获取 JSON 中某个字段
JSONObject jsonObject = JSONObject.parseObject(responseStr);
String success = jsonObject.getString("isSuccess");
Integer code = jsonObject.getInteger("code");
}
6.4 常用注解示例
@Data
public class DeliveryOrderDto {
private Integer id;
// JSON key 与 Java 字段名不同
@JsonProperty("delivery_code")
private String deliveryCode;
// 序列化时忽略(内部使用,不返回前端)
@JsonIgnore
private Date achieveUserOrderCutDate;
// 日期格式化
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime;
// null 值不序列化到 JSON 中
@JsonInclude(JsonInclude.Include.NON_NULL)
private String remark;
// 反序列化时接受多个字段名(兼容不同上游系统)
@JsonAlias({"shuttleBus", "shuttle_bus", "busSchedule"})
private List<String> shuttleBus;
}
// 序列化结果:
// {"id":1,"delivery_code":"SO.001","createTime":"2026-08-05 10:00:00","shuttleBus":["1","3","5"]}
// 注意:achieveUserOrderCutDate 被忽略,remark 为 null 时不输出
6.5 驼峰与下划线自动转换
/**
* 对接第三方系统时,对方用下划线命名,我方用驼峰.
*/
// 方式1:全局配置 ObjectMapper
ObjectMapper snakeMapper = new ObjectMapper()
.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
// Java 对象 {"orderCode":"SO.001"} → JSON {"order_code":"SO.001"}
String json = snakeMapper.writeValueAsString(dto);
// 方式2:类级别注解
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class ThirdPartyResponseDto {
private String orderCode; // JSON 中对应 "order_code"
private Integer deliveryWay; // JSON 中对应 "delivery_way"
}
七、关键设计总结
| 设计要点 | 实现方式 | 收益 |
|---|---|---|
| ObjectMapper 单例 | 静态 final 全局复用 | 避免重复创建的性能开销 |
| 忽略未知字段 | FAIL_ON_UNKNOWN_PROPERTIES=false | 上游加字段不影响下游反序列化 |
| 泛型反序列化 | TypeReference | 正确还原嵌套泛型类型 |
| 日期处理 | @JsonFormat | 统一日期格式,避免前端解析问题 |
| 空值控制 | @JsonInclude(NON_NULL) | 减少传输数据量 |
| 异常容错 | try-catch 返回 null/空字符串 | 序列化失败不阻断业务 |
| 命名策略 | SnakeCase / CamelCase | 适配不同系统的命名风格 |
到此这篇关于Java中JSON序列化与反序列化的原理与实践方法的文章就介绍到这了,更多相关Java JSON序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
