Spring Boot 使用自定义 JsonDeserializer 同时支持多种日期格式

使用自定义 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. 方案的优点总结

  1. 向后兼容

    老接口无需改动。

  2. 不影响其它字段

    只对指定字段生效,不会影响全局日期解析。

  3. 代码可维护性高

    添加更多格式非常方便,只需要在数组里多加一条。

  4. 错误提示明确

    解析失败时能清晰抛出异常,便于排查问题。

6. 什么时候应该使用这个方案?

如果你的项目出现以下情况,这个方案就特别适合:

  • 不同前端传参格式不一致

  • 老系统与新系统混用日期格式

  • 字段本质上是一个 Date,需要兼容多种输入方式

  • 想保持 DTO 字段仍然是 Date,而不是 String 或 LocalDateTime

如果你的项目中这种日期字段更多,也可以把此反序列化器注册为 全局解析器,统一管理。

7. 总结

Spring Boot 默认的 Jackson 日期解析是单格式 的,无法兼容复杂的前端情况。

通过自定义 JsonDeserializer,我们可以让某个字段支持多种日期格式,同时保持后端代码整洁、可维护。

这是一个非常实用的小技巧,几乎所有团队都会遇到这样的需求,建议收藏和长期使用。

相关推荐
bcbnb31 分钟前
Charles抓包在复杂系统中的应用,高难度问题的诊断与验证方法
后端
tan180°36 分钟前
Linux网络IP(下)(16)
linux·网络·后端·tcp/ip
牢七40 分钟前
Javan
java
A***279542 分钟前
ReactGraphQL案例
spring boot·spring cloud·柔性数组
非优秀程序员43 分钟前
教程:如何修改 Docker 容器 bisheng-frontend 中的静态文件
后端
元Y亨H1 小时前
深入解析 @DataScope 数据权限注解
spring boot
我叫黑大帅1 小时前
六边形架构?小白也能秒懂的「抗造代码秘诀」
java·后端·架构
kevinzeng1 小时前
结合Condition实现生产者与消费者示例,来进一步分析AbstractQueuedSynchronizer的内部工作机制
后端
不穿格子的程序员1 小时前
Java基础篇——JDK新特性总结
java·虚拟线程·jdk新特性