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

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

相关推荐
JWASX24 分钟前
【Go】map 源码解析 - group
后端·golang·map·源码分析
文丑颜不良啊30 分钟前
LeetCode2141~2160题目解析
java·leetcode
LabVIEW开发37 分钟前
LabVIEW 连接板:28 个端子怎么排,优先级怎么定
java·算法·labview
天天被压力1 小时前
【A股分钟级高频数据完全手册 #07】北交所历史分钟K线接口:京市Pro版分时批量
java·人工智能·python
天天被压力1 小时前
【A股分钟级高频数据完全手册 #08】股票日、分时数据接口对比:普通分时与Pro版差异实测
java·人工智能·python
量化分析码农1 小时前
【Python量化波动率实战 01】波动率是怎么算的:年化、滚动与区间估计一次讲清
后端
特立独行的猫A2 小时前
AI 时代,我为什么推荐用 cj-tauri 开发桌面应用
java·后端·python
金銀銅鐵2 小时前
从一个类名到一张类图:用反射自动生成 PlantUML
java·后端·uml
小郎君6582 小时前
注解式脱敏:用 Jackson 的 ContextualSerializer 做字段级策略(一)
spring boot·spring cloud
追逐时光者2 小时前
一款基于 .NET 开源、免费的数据库自治诊断平台
后端·.net