使用自定义 JsonDeserializer 同时支持多种日期格式
- 背景
- [1. 为什么 @JsonFormat 无法解决这个问题?](#1. 为什么 @JsonFormat 无法解决这个问题?)
- [2. 自定义日期反序列化器](#2. 自定义日期反序列化器)
-
- [2.1 编写自定义解析器](#2.1 编写自定义解析器)
- [3. 让字段使用自定义解析器](#3. 让字段使用自定义解析器)
- [4. 使用效果](#4. 使用效果)
- [5. 方案的优点总结](#5. 方案的优点总结)
- [6. 什么时候应该使用这个方案?](#6. 什么时候应该使用这个方案?)
- [7. 总结](#7. 总结)
背景
在实际业务开发中,后端经常遇到一个尴尬的问题:
-
有前端传 "2025-11-15 10:00:00"
-
有些前端只传 "2025-11-15"
而后端字段是一个 Date 类型,默认 Jackson 只能按一个格式解析,这就导致:
yaml
Invalid JSON input: Cannot deserialize value of type java.util.Date
这篇文章将介绍一种干净优雅的方式:通过自定义 Jackson 反序列化器,让一个字段同时支持多种日期格式。
1. 为什么 @JsonFormat 无法解决这个问题?
假设你写了:
java
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
那么 Jackson 只允许格式:
yaml
2025-11-15 10:00:00
如果前端传:
yaml
2025-11-15
仍然会报错。
原因:
@JsonFormat 不支持多模式解析
只能配置一个格式,无法 fallback。
因此想兼容多格式,必须上 自定义反序列化器。
2. 自定义日期反序列化器
2.1 编写自定义解析器
创建一个类 MultiDateDeserializer:
java
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MultiDateDeserializer extends JsonDeserializer<Date> {
private static final String[] patterns = new String[]{
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd"
};
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
if (StrUtil.isEmpty(p.getText())) {
return null;
}
String value = p.getText().trim();
for (String pattern : patterns) {
try {
return new SimpleDateFormat(pattern).parse(value);
} catch (Exception ignored) {
}
}
throw new RuntimeException("日期格式不合法: " + value);
}
}
✔ 支持多个格式
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd
✔ 自动容错
只要符合其中一个格式就自动解析。
3. 让字段使用自定义解析器
在 DTO 中直接指定:
java
@JsonDeserialize(using = MultiDateDeserializer.class)
private Date startTime;
此时,这个字段已经具备以下能力:
| 前端入参 | 能否解析 | 示例 |
|---|---|---|
| 完整时间 | ✔ | 2025-11-15 10:00:00 |
| 仅日期 | ✔ | 2025-11-15 |
| 其它格式 | ❌ | 2025/11/15 |
当格式不合法时后端会明确抛出错误,防止脏数据进入系统。
4. 使用效果
前端传:
json
{
"startTime": "2025-11-15"
}
或:
json
{
"startTime": "2025-11-15 10:00:00"
}
Spring Boot 都能优雅解析成 java.util.Date。
再也不会出现:
java
InvalidFormatException: expected format "yyyy-MM-dd HH:mm:ss"
5. 方案的优点总结
-
向后兼容
老接口无需改动。
-
不影响其它字段
只对指定字段生效,不会影响全局日期解析。
-
代码可维护性高
添加更多格式非常方便,只需要在数组里多加一条。
-
错误提示明确
解析失败时能清晰抛出异常,便于排查问题。
6. 什么时候应该使用这个方案?
如果你的项目出现以下情况,这个方案就特别适合:
-
不同前端传参格式不一致
-
老系统与新系统混用日期格式
-
字段本质上是一个 Date,需要兼容多种输入方式
-
想保持 DTO 字段仍然是 Date,而不是 String 或 LocalDateTime
如果你的项目中这种日期字段更多,也可以把此反序列化器注册为 全局解析器,统一管理。
7. 总结
Spring Boot 默认的 Jackson 日期解析是单格式 的,无法兼容复杂的前端情况。
通过自定义 JsonDeserializer,我们可以让某个字段支持多种日期格式,同时保持后端代码整洁、可维护。
这是一个非常实用的小技巧,几乎所有团队都会遇到这样的需求,建议收藏和长期使用。