一文详解JackSon配置信息
Published on 2022-06-24 14:19 in 暂未分类 with 无涯Ⅱ

一文详解JackSon配置信息

    背景

    1.1 问题

    Spring Boot 在处理对象的序列化和反序列化时,默认使用框架自带的JackSon配置。使用框架默认的,通常会面临如下问题:
    1. Date返回日期格式(建议不使用Date,但老项目要兼容),带有T,如 2018-05-15T24:59:59:
    1. LocalDate返回日期对象为数组(框架中继承了 WebMvcConfigurationSupport);
    1. LocalDateTime时间转换失败等;
    1. 定义了日期类型,如LocalDate,前端对接时(post/get),如果传入日期字符串("2022-05-05"),会报String 转换为LocalDate失败;
    1. 返回long型数据,前端js存在精度问题,需做转换;
    1. 一些特殊对象要做业务特殊转换,如加解密等;

    1.2 解决方案

    针对上述问题,存在很多种解决方案。由于底层框架统一配置拦截类实现的模式不同,还是会存在差异,本文主要说明在不同的配置场景下,自定义Jackson配置的一些注意事项和差异化原因:
    为了解决特殊对象(如日期)的序列化和反序列化问题,常用方案如下:
    1. 针对特殊的具体对象,在对象上面使用注解,如:
    @JsonSerialize(using= JsonDateSerializer.class)
    private Date taskEndTime;
    
    @ApiModelProperty(value = "检查日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate checkDate;
    1. 重新实现WebMvcConfigurer接口,自定义JackSon配置。
    1. 继承 WebMvcConfigurationSupport类,自定义JackSon配置。

    1.3 特别说明

    • 方案1的模式,在对应变量上加上注解,是可以解决问题,但是严重编码重复,不优雅;
    • 实现WebMvcConfigurer接口与继承WebMvcConfigurationSupport类,是Spring Boot提供开发者做统全局配置类的两种模式,注意两种模式的差异,详情查看后续章节介绍(两种不同的模式,使用不当时,就会出现配置不生效的情况);

    自定义Jackson

    1. JackSon配置说明

    自定义一个Jackson配置信息,需要了解Jackson的一些配置标准,如:
    复制代码
    //在反序列化时忽略在 json 中存在但 Java 对象不存在的属性
    
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
    
    false);
    
    //在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ ,比如如果一个类中有private Date date;这种日期属性,序列化后为:{"date" : 1413800730456},若不为true,则为{"date" : "2014-10-20T10:26:06.604+0000"}
    
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
    
    //在序列化时忽略值为 null 的属性
    
    mapper.setSerializationInclusion(Include.NON_NULL);
    
    //忽略值为默认值的属性
    
    mapper.setDefaultPropertyInclusion(Include.NON_DEFAULT);
    
    // 美化输出
    
    mapper.enable(SerializationFeature.INDENT_OUTPUT);
    
    // 允许序列化空的POJO类
    
    // (否则会抛出异常)
    
    mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
    
    // 把java.util.Date, Calendar输出为数字(时间戳)
    
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    
    // 在遇到未知属性的时候不抛出异常
    
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    
    // 强制JSON 空字符串("")转换为null对象值:
    
    mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
    
    // 在JSON中允许C/C++ 样式的注释(非标准,默认禁用)
    
    mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
    
    // 允许没有引号的字段名(非标准)
    
    mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
    
    // 允许单引号(非标准)
    
    mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
    
    // 强制转义非ASCII字符
    
    mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
    
    // 将内容包裹为一个JSON属性,属性名由@JsonRootName注解指定
    
    mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
    
    //序列化枚举是以toString()来输出,默认false,即默认以name()来输出
    
    mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
    
    //序列化Map时对key进行排序操作,默认false
    
    mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true);
    
    //序列化char[]时以json数组输出,默认false
    
    mapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);
    View Code
    复制代码
    1. 实现WebMvcConfigurer接口

    重新编写一个ObjectMapper,替换系统默认的bean,就可以实现接口在post请求模式时,对象序列化与反序列化走子定义配置信息了。
    重新编写Jackson后,并不能处理get请求时,日期等特殊对象的序列化处理;针对get请求,编写对象的序列化规则函数,通过实现addFormatters()接口,可扩展支持;

    编写LocalDateTime转换函数

    复制代码
    /**
     * java 8 LocalDateTime转换器
     *
     * @author wangling
     */
    public class LocalDateTimeFormatter implements Formatter<LocalDateTime> {
        private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
        @Override
        public LocalDateTime parse(String text, Locale locale) throws ParseException {
            return LocalDateTime.parse(text, formatter);
        }
    
        @Override
        public String print(LocalDateTime object, Locale locale) {
            return formatter.format(object);
        }
    }
    复制代码

    编写LocalDate转换函数

    复制代码
    /**
     * java 8 localDate转换器
     *
     * @author wangling
     */
    public class LocalDateFormatter implements Formatter<LocalDate> {
        private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    
        @Override
        public LocalDate parse(String text, Locale locale) throws ParseException {
            return LocalDate.parse(text, formatter);
        }
    
        @Override
        public String print(LocalDate object, Locale locale) {
            return formatter.format(object);
        }
    }
    复制代码

    编写Jackson配置

    编写一个自定义的ObjectMapper bean对象,设置优先级替换默认bean。
    复制代码
    /**
     * 项目全局配置类
     * 
     * @author wangling
     * @date 2022/06/10
     */
    @Configuration
    @RequiredArgsConstructor
    public class WebConfig implements WebMvcConfigurer {
    
      @Override
      public void addFormatters(FormatterRegistry registry) {
        registry.addFormatterForFieldType(LocalDate.class, new LocalDateFormatter());
        registry.addFormatterForFieldType(LocalDateTime.class, new LocalDateTimeFormatter());
      }
    
      @Bean
      @Primary
      public ObjectMapper ObjectMapper() {
        String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
        String dateFormat = "yyyy-MM-dd";
        String timeFormat = "HH:mm:ss";
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 序列化
        javaTimeModule.addSerializer(
            LocalDateTime.class,
            new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
        javaTimeModule.addSerializer(
            LocalDate.class,
            new LocalDateSerializer(DateTimeFormatter.ofPattern(dateFormat)));
        javaTimeModule.addSerializer(
            LocalTime.class,
            new LocalTimeSerializer(DateTimeFormatter.ofPattern(timeFormat)));
        javaTimeModule.addSerializer(
            Date.class,
            new DateSerializer(false, new SimpleDateFormat(dateTimeFormat)));
    
        // 反序列化
        javaTimeModule.addDeserializer(
            LocalDateTime.class,
            new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
        javaTimeModule.addDeserializer(
            LocalDate.class,
            new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
        javaTimeModule.addDeserializer(
            LocalTime.class,
            new LocalTimeDeserializer(DateTimeFormatter.ofPattern(timeFormat)));
        javaTimeModule.addDeserializer(Date.class, new DateDeserializers.DateDeserializer() {
          @SneakyThrows
          @Override
          public Date deserialize(JsonParser jsonParser, DeserializationContext dc) {
            String text = jsonParser.getText().trim();
            SimpleDateFormat sdf = new SimpleDateFormat(dateTimeFormat);
            return sdf.parse(text);
          }
        });
        javaTimeModule.addSerializer(Long.class, ToStringSerializer.instance);
        javaTimeModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
        objectMapper.registerModule(javaTimeModule);
        return objectMapper;
      }
    }
    复制代码
    1. WebMvcConfigurationSupport类

    编写Jackson配置

    重新编写Jackson后,并不能处理get请求时,日期等特殊对象的序列化处理;针对get请求,编写对象的序列化规则函数,通过实现addFormatters()接口,可扩展支持;
    编写自定义配置Jackson信息时,需要重写extendMessageConverters方法。具体技术细节原因,请参考文档《Spring Boot实现WebMvcConfigurationSupport导致自定义的JSON时间返回格式不生效》
    复制代码
    /**
     * 项目全局配置类
     * 
     * @author wangling
     * @date 2022/06/10
     */
    @Configuration
    public class MvcInterceptorConfig extends WebMvcConfigurationSupport {
    
      @Override
      protected void addFormatters(FormatterRegistry registry) {
        // 用于get 全局格式化日期转换
        registry.addFormatterForFieldType(LocalDate.class, new LocalDateFormatter());
        registry.addFormatterForFieldType(LocalDateTime.class, new LocalDateTimeFormatter());
      }
    
      @Override
      protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 代替框架默认的JackSon配置 用于post 全局格式化日期转换,long转字符串
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter =
            new MappingJackson2HttpMessageConverter();
        jackson2HttpMessageConverter.setObjectMapper(ObjectMapper());
        // 基于顺序,先执行自定义的
        converters.add(0, jackson2HttpMessageConverter);
      }
    
    
      private ObjectMapper ObjectMapper() {
        String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
        String dateFormat = "yyyy-MM-dd";
        String timeFormat = "HH:mm:ss";
        ObjectMapper objectMapper = new ObjectMapper();
        //忽略空Bean转json的错误
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        //忽略 在json字符串中存在,但是在对象中不存在对应属性的情况,防止错误。
        // 例如json数据中多出字段,而对象中没有此字段。如果设置true,抛出异常,因为字段不对应;false则忽略多出的字段,默认值为null,将其他字段反序列化成功
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        // 序列化
        javaTimeModule.addSerializer(
            LocalDateTime.class,
            new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
        javaTimeModule.addSerializer(
            LocalDate.class,
            new LocalDateSerializer(DateTimeFormatter.ofPattern(dateFormat)));
        javaTimeModule.addSerializer(
            LocalTime.class,
            new LocalTimeSerializer(DateTimeFormatter.ofPattern(timeFormat)));
        javaTimeModule.addSerializer(
            Date.class,
            new DateSerializer(false, new SimpleDateFormat(dateTimeFormat)));
    
        // 反序列化
        javaTimeModule.addDeserializer(
            LocalDateTime.class,
            new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
        javaTimeModule.addDeserializer(
            LocalDate.class,
            new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
        javaTimeModule.addDeserializer(
            LocalTime.class,
            new LocalTimeDeserializer(DateTimeFormatter.ofPattern(timeFormat)));
        javaTimeModule.addDeserializer(Date.class, new DateDeserializers.DateDeserializer() {
          @SneakyThrows
          @Override
          public Date deserialize(JsonParser jsonParser, DeserializationContext dc) {
            String text = jsonParser.getText().trim();
            SimpleDateFormat sdf = new SimpleDateFormat(dateTimeFormat);
            return sdf.parse(text);
          }
        });
        javaTimeModule.addSerializer(Long.class, ToStringSerializer.instance);
        javaTimeModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
        objectMapper.registerModule(javaTimeModule);
        return objectMapper;
      }
    }
    View Code
    复制代码

    WebMvcConfigurer与WebMvcConfigurationSupport相关知识点

    1. 基础知识点

    Spring的 WebMvcConfigurer 接口提供了很多方法让开发者来定制SpringMVC的配置。
    WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware。支持的自定义的配置更多更全,WebMvcConfigurerAdapter有的方法,这个类也都有。该类注释内容翻译:这是提供MVC Java config 背后配置的主要类。 通常是通过将@EnableWebMvc添加到应用程序的@Configuration类中来导入的。 另一个更高级的选择是直接从此类扩展并在需要时重写方法,记住子类要添加@Configuration,重写带有@Bean的方法也要加上@Bean。
    1. 使用注意事项

              参考文档:《拦截失效原因》
    1. 实现WebMvcConfigurer: 不会覆盖WebMvcAutoConfiguration的配置
    1. 实现WebMvcConfigurer+注解@EnableWebMvc:会覆盖WebMvcAutoConfiguration的配置
    1. 继承WebMvcConfigurationSupport:会覆盖WebMvcAutoConfiguration的配置
    1. 继承DelegatingWebMvcConfiguration:会覆盖WebMvcAutoConfiguration的配置
    1. 推荐使用模式

    1. 非必要,最好避免WebMvcConfigurer,WebMvcConfigurationSupport在一个项目中同时使用;
    1. 出于安全性拦截配置,建议项目采用WebMvcConfigurer接口的方式做全局配置;
    1. 日期,时间等建议使用LocalDate,替换历史的Date数据类型;
     
    posted @   无涯Ⅱ  阅读(1272)  评论(3编辑  收藏  举报
    相关博文:
    阅读排行:
    · 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
    · 单线程的Redis速度为什么快?
    · SQL Server 2025 AI相关能力初探
    · 展开说说关于C#中ORM框架的用法!
    · AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
    点击右上角即可分享
    微信分享提示