统一 JSON 格式,JacksonObjectMapper 定制 Spring Boot JSON 转换规则

在 Spring Boot 前后端分离项目中,JSON 是数据交互的核心格式,而默认的 JSON 转换规则往往难以适配实际业务需求:日期时间返回乱码、前端多传字段接口直接报错...... 这些问题会严重影响开发效率和系统稳定性。

项目中自定义的JacksonObjectMapper配置类,正是解决这些痛点的核心方案。它基于 Jackson 框架扩展,通过定制序列化 / 反序列化规则,配合 Spring MVC 消息转换器注册,成为项目 JSON 交互的 "专属翻译官",既保证数据格式统一,又大幅提升前后端交互的容错性。今天就详解这个类的设计思路、核心功能和生效原理。

一、核心痛点:Spring Boot 默认 JSON 转换的两大问题

在未做自定义配置时,Spring Boot 使用 Jackson 默认的ObjectMapper处理 JSON 转换,在实际开发中会遇到两个典型问题,直接影响前后端交互:

  1. 日期时间格式混乱LocalDateTime/LocalDate等时间类型转 JSON 时,会变成[2024,1,23,12,30]这样的数组格式,前端解析繁琐,且无统一格式规范;
  2. 容错性极差 :前端传参若包含后端实体类中不存在的字段,Jackson 会直接抛出UnrecognizedPropertyException异常,导致接口直接挂掉,一点多余参数都无法容忍。

JacksonObjectMapper的核心作用,就是通过自定义配置解决这两个痛点,让 JSON 转换完全适配项目的业务需求。

二、定制者:JacksonObjectMapper------ 重写 JSON 转换规则

JacksonObjectMapper继承自 Jackson 框架的核心类ObjectMapper,是整个 JSON 定制化的核心类。它通过构造函数完成核心配置,主要做了两件关键事:统一时间类型转换规则、开启未知属性忽略,从根本上解决默认转换的痛点。

核心设计思路

  1. 针对时间类型:为LocalDateTimeLocalDateLocalTime分别注册自定义序列化器反序列化器,强制规定 JSON 转换的格式;
  2. 针对容错性:关闭 "未知属性报错" 开关,让后端自动忽略前端传来的多余字段;
  3. 完全继承ObjectMapper原有功能,仅扩展定制化规则,不影响正常的 JSON 转换逻辑。

完整代码实现

java 复制代码
package com.sky.json;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

/**
 * 自定义Jackson对象映射器,定制JSON序列化/反序列化规则
 */
public class JacksonObjectMapper extends ObjectMapper {

    // 定义统一的时间格式常量
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm";
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        // 1. 开启容错配置:忽略后端实体中不存在的前端传参字段
        this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 2. 创建SimpleModule,用于注册自定义的序列化器/反序列化器
        SimpleModule simpleModule = new SimpleModule();

        // 3. 为LocalDateTime注册序列化器(Java→JSON)和反序列化器(JSON→Java)
        simpleModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        simpleModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));

        // 4. 为LocalDate注册序列化器和反序列化器
        simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        simpleModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));

        // 5. 为LocalTime注册序列化器和反序列化器
        simpleModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        simpleModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        // 6. 将自定义模块注册到ObjectMapper中
        this.registerModule(simpleModule);
    }
}

关键配置详解

  1. 时间格式定制 :通过SimpleModule注册 Jackson 提供的时间类型序列化 / 反序列化器,指定统一格式 ------LocalDateTimeyyyy-MM-dd HH:mm(贴合业务需求,仅保留到分钟)、LocalDateyyyy-MM-ddLocalTimeHH:mm:ss,确保前后端时间格式完全统一;
  2. 容错性配置this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)是核心容错配置,关闭未知属性报错,前端多传字段时,后端仅解析匹配字段,多余字段直接忽略;
  3. 常量定义:将时间格式定义为常量,便于后续项目统一修改,避免硬编码,提升可维护性。

三、注册者:WebMvcConfiguration------ 让自定义规则生效

仅仅实现JacksonObjectMapper类,并不会让其生效,因为 Spring MVC 依然会使用默认的消息转换器处理 JSON。想要让自定义规则替代默认规则,需要在Web 配置类 中扩展 Spring MVC 的消息转换器,将JacksonObjectMapper注册进去,并设置为最高优先级

核心原理

Spring MVC 通过 **HttpMessageConverter(消息转换器)** 处理请求和响应的数据转换,其中MappingJackson2HttpMessageConverter是专门处理 JSON 的转换器。我们需要创建一个自定义的该转换器,绑定JacksonObjectMapper,并将其加入 Spring MVC 的转换器列表第一位,让 Spring MVC 优先使用自定义规则。

核心代码实现

java 复制代码
package com.sky.config;

import com.sky.json.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.List;

/**
 * Spring MVC Web配置类,扩展消息转换器
 */
@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    /**
     * 扩展Spring MVC的消息转换器,注册自定义的JacksonObjectMapper
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("开始注册自定义JSON消息转换器...");
        // 1. 创建专门处理JSON的消息转换器
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        // 2. 为转换器绑定自定义的JacksonObjectMapper
        converter.setObjectMapper(new JacksonObjectMapper());
        // 3. 将自定义转换器添加到列表首位,设置最高优先级
        converters.add(0, converter);
    }
}

关键步骤说明

  1. 继承 WebMvcConfigurationSupport :这是 Spring MVC 扩展配置的标准方式,重写其extendMessageConverters方法即可自定义消息转换器;
  2. 创建自定义转换器 :实例化MappingJackson2HttpMessageConverter,并通过setObjectMapper方法绑定我们的JacksonObjectMapper,让该转换器使用自定义规则;
  3. 设置最高优先级converters.add(0, converter)是核心步骤,将自定义转换器加入列表索引 0 的位置(第一位),Spring MVC 在处理 JSON 时会优先使用该转换器,完全替代默认转换器。

四、执行流程:自定义 JSON 转换的完整链路

JacksonObjectMapper完成注册后,项目中所有的 JSON 转换(@RequestBody请求解析、@ResponseBody响应返回)都会遵循自定义规则,整个流程完全自动化,对开发者无感知,具体执行步骤如下:

  1. 触发 JSON 转换 :前端发送请求(@RequestBody接收 JSON 参数)或后端返回数据(@ResponseBody返回 JSON),Spring MVC 识别到需要处理 JSON 格式;
  2. 查找消息转换器 :Spring MVC 遍历converters消息转换器列表,寻找能处理 JSON 的转换器;
  3. 命中自定义转换器 :由于自定义的MappingJackson2HttpMessageConverter在列表第一位,会被优先选中;
  4. 执行定制化转换 :转换器内部调用绑定的JacksonObjectMapper,按照自定义规则完成序列化(Java→JSON)或反序列化(JSON→Java);
  5. 返回转换结果:时间类型按统一格式转换为字符串,多余字段被自动忽略,最终返回符合要求的 JSON 数据。

效果示例

  1. 时间类型序列化 :后端LocalDateTime对象2024-01-23 12:30:00,前端接收到的 JSON 为"2024-01-23 12:30",格式统一且易解析;
  2. 未知属性容错 :前端传参{"name":"张三","age":20,"gender":"男"},后端实体仅包含nameage字段,接口正常执行,gender字段被自动忽略,无任何异常。

五、核心优势:为什么要自定义 JacksonObjectMapper?

相比 Spring Boot 的默认 JSON 转换规则,自定义的JacksonObjectMapper在实际项目中展现出三大核心优势,完全适配企业级开发需求:

  1. 格式统一,降低前后端沟通成本:强制规定所有时间类型的 JSON 转换格式,前端无需做复杂的格式解析,前后端按统一规范开发,避免因格式问题产生的 bug;
  2. 容错性提升,系统更健壮:忽略前端多余传参,即使前端因需求变更临时多传字段,后端接口也不会报错,大幅提升系统的兼容性和健壮性,尤其适合快速迭代的项目;
  3. 可扩展性强,适配业务变化 :若后续项目需要修改时间格式(如增加秒数),只需修改JacksonObjectMapper中的格式常量,无需修改任何业务代码,全局生效;若需要新增自定义转换规则,只需在该类中继续注册序列化器 / 反序列化器即可;
  4. 无侵入式设计,不影响原有代码 :所有配置都集中在专用配置类中,业务代码(Controller、Service)无需做任何修改,只需正常使用@RequestBody/@ResponseBody即可,符合 "开闭原则"。

六、扩展场景:JacksonObjectMapper 的更多定制化方向

在实际项目中,可根据业务需求,在JacksonObjectMapper中继续扩展更多 JSON 转换规则,让其功能更强大:

  1. 空值处理 :配置空值的序列化规则,例如将null值序列化为空字符串""或空数组[],避免前端接收到null值出现解析错误;
  2. 数值类型处理:对 Long 类型进行特殊处理,避免前端解析大数值时出现精度丢失(如雪花算法生成的 ID);
  3. 枚举类型处理:为枚举类注册自定义序列化器,让枚举类型按 "枚举值" 或 "枚举描述" 序列化,而非默认的枚举名称;
  4. 自定义对象处理:为项目中的自定义实体类注册序列化器,定制其 JSON 转换格式,适配特殊业务需求。

空值处理扩展示例

JacksonObjectMapper的构造函数中添加以下配置,即可将null值序列化为空字符串:

java 复制代码
// 配置空值序列化规则
this.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
    @Override
    public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString("");
    }
});

七、总结

JacksonObjectMapper是 Spring Boot 前后端分离项目中JSON 转换定制化 的经典实现,它的核心价值在于将 JSON 转换规则从框架默认配置中抽离,通过自定义配置完全适配项目的业务需求

它作为项目 JSON 交互的 "专属翻译官",统一了时间类型的转换格式,让前后端数据交互更规范;作为 "安检员",自动过滤前端传来的多余参数,让系统更健壮、容错性更强。而通过 Spring MVC 的WebMvcConfiguration注册为最高优先级转换器,让整个自定义规则全局生效,实现了无侵入式的功能增强。

在实际开发中,这种 "集中式定制框架默认规则" 的思想非常重要,不仅适用于 JSON 转换,还可应用于日期格式化、日志配置、异常处理等场景。掌握这种思想,能让我们的项目更贴合业务需求,同时提升代码的可维护性和系统的健壮性。

相关推荐
你才是臭弟弟4 小时前
SpringBoot 集成MinIo(根据上传文件.后缀自动归类)
java·spring boot·后端
what丶k5 小时前
SpringBoot3 配置文件使用全解析:从基础到实战,解锁灵活配置新姿势
java·数据库·spring boot·spring·spring cloud
RwTo5 小时前
【源码】- SpringBoot启动
java·spring boot·spring
Elieal5 小时前
JWT 登录校验机制:5 大核心类打造 Spring Boot 接口安全屏障
spring boot·后端·安全
czlczl200209255 小时前
Spring Boot Filter :doFilter 与 doFilterInternal 的差异
java·spring boot·后端
码界奇点5 小时前
基于Spring Boot和Activiti6的工作流OA系统设计与实现
java·spring boot·后端·车载系统·毕业设计·源代码管理
yangminlei5 小时前
Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现与实战指南
java·spring boot·后端
czlczl200209256 小时前
Spring Boot :彻底解决 HttpServletRequest 输入流只能读取一次的问题
java·spring boot·后端
小信丶6 小时前
@MappedJdbcTypes 注解详解:应用场景与实战示例
java·数据库·spring boot·后端·mybatis
Sylvia33.6 小时前
足球“文字直播/事件流”API详解:解码比赛的数字DNA
java·服务器·前端·json