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,我们可以让某个字段支持多种日期格式,同时保持后端代码整洁、可维护。

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

相关推荐
狂炫冰美式4 分钟前
《预言市场进化论:从罗马斗兽场,到 Polymarket 的 K 线图》
前端·后端
程序员卷卷狗5 分钟前
Java 单例模式的五种实现:饿汉式、懒汉式、DCL、静态内部类、枚举单例
java·开发语言·单例模式
@淡 定7 分钟前
动态代理(JDK动态代理/CGLIB动态代理
java·开发语言·python
悟能不能悟14 分钟前
java 判断string[]中是否有a
java·开发语言
4***149015 分钟前
高并发时代的“确定性”挑战——为何稳定性正在成为 JVM 的下一场核心竞争?
java·开发语言·jvm
hahjee19 分钟前
Go编写的ANSI终端颜色和样式控制库在OpenHarmony PC上的完整适配实战
开发语言·后端·鸿蒙
野蛮人6号27 分钟前
黑马微服务p10mybatisplus09核心功能iservice 测试文档无法正常打开
java·黑马微服务
危险、28 分钟前
《Java Stream 中 toMap 的生产级用法:一次 Duplicate key 的异常问题复盘》
java
古城小栈33 分钟前
Java 内存优化:JDK 22 ZGC 垃圾收集器调优
java·python·算法
福大大架构师每日一题36 分钟前
rust 1.92.0 更新详解:语言特性增强、编译器优化与全新稳定API
java·javascript·rust