背景

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. 针对特殊的具体对象,在对象上面使用注解,如:
  1. @JsonSerialize(using= JsonDateSerializer.class)
  2. private Date taskEndTime;
  3.  
  4. @ApiModelProperty(value = "检查日期")
  5. @JsonFormat(pattern = "yyyy-MM-dd")
  6. private LocalDate checkDate;
  1. 重新实现WebMvcConfigurer接口,自定义JackSon配置。
  1. 继承 WebMvcConfigurationSupport类,自定义JackSon配置。

1.3 特别说明

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

自定义Jackson

  1. JackSon配置说明

自定义一个Jackson配置信息,需要了解Jackson的一些配置标准,如:

  1. //在反序列化时忽略在 json 中存在但 Java 对象不存在的属性
  2.  
  3. mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
  4.  
  5. false);
  6.  
  7. //在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ ,比如如果一个类中有private Date date;这种日期属性,序列化后为:{"date" : 1413800730456},若不为true,则为{"date" : "2014-10-20T10:26:06.604+0000"}
  8.  
  9. mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
  10.  
  11. //在序列化时忽略值为 null 的属性
  12.  
  13. mapper.setSerializationInclusion(Include.NON_NULL);
  14.  
  15. //忽略值为默认值的属性
  16.  
  17. mapper.setDefaultPropertyInclusion(Include.NON_DEFAULT);
  18.  
  19. // 美化输出
  20.  
  21. mapper.enable(SerializationFeature.INDENT_OUTPUT);
  22.  
  23. // 允许序列化空的POJO类
  24.  
  25. // (否则会抛出异常)
  26.  
  27. mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
  28.  
  29. // 把java.util.Date, Calendar输出为数字(时间戳)
  30.  
  31. mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  32.  
  33. // 在遇到未知属性的时候不抛出异常
  34.  
  35. mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
  36.  
  37. // 强制JSON 空字符串("")转换为null对象值:
  38.  
  39. mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
  40.  
  41. // 在JSON中允许C/C++ 样式的注释(非标准,默认禁用)
  42.  
  43. mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
  44.  
  45. // 允许没有引号的字段名(非标准)
  46.  
  47. mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
  48.  
  49. // 允许单引号(非标准)
  50.  
  51. mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
  52.  
  53. // 强制转义非ASCII字符
  54.  
  55. mapper.configure(JsonGenerator.Feature.ESCAPE_NON_ASCII, true);
  56.  
  57. // 将内容包裹为一个JSON属性,属性名由@JsonRootName注解指定
  58.  
  59. mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
  60.  
  61. //序列化枚举是以toString()来输出,默认false,即默认以name()来输出
  62.  
  63. mapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
  64.  
  65. //序列化Map时对key进行排序操作,默认false
  66.  
  67. mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS,true);
  68.  
  69. //序列化char[]时以json数组输出,默认false
  70.  
  71. mapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);
  72.  
  73. //序列化BigDecimal时之间输出原始数字还是科学计数,默认false,即是否以toPlainString()科学计数方式来输出
  74.  
  75. mapper.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS,true);
  1. 实现WebMvcConfigurer接口

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

编写LocalDateTime转换函数

  1. /**
  2. * java 8 LocalDateTime转换器
  3. *
  4. * @author wangling
  5. */
  6. public class LocalDateTimeFormatter implements Formatter<LocalDateTime> {
  7. private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
  8.  
  9. @Override
  10. public LocalDateTime parse(String text, Locale locale) throws ParseException {
  11. return LocalDateTime.parse(text, formatter);
  12. }
  13.  
  14. @Override
  15. public String print(LocalDateTime object, Locale locale) {
  16. return formatter.format(object);
  17. }
  18. }

编写LocalDate转换函数

  1. /**
  2. * java 8 localDate转换器
  3. *
  4. * @author wangling
  5. */
  6. public class LocalDateFormatter implements Formatter<LocalDate> {
  7. private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
  8.  
  9. @Override
  10. public LocalDate parse(String text, Locale locale) throws ParseException {
  11. return LocalDate.parse(text, formatter);
  12. }
  13.  
  14. @Override
  15. public String print(LocalDate object, Locale locale) {
  16. return formatter.format(object);
  17. }
  18. }

编写Jackson配置

编写一个自定义的ObjectMapper bean对象,设置优先级替换默认bean。
  1. /**
  2. * 项目全局配置类
  3. *
  4. * @author wangling
  5. * @date 2022/06/10
  6. */
  7. @Configuration
  8. @RequiredArgsConstructor
  9. public class WebConfig implements WebMvcConfigurer {
  10.  
  11. @Override
  12. public void addFormatters(FormatterRegistry registry) {
  13. registry.addFormatterForFieldType(LocalDate.class, new LocalDateFormatter());
  14. registry.addFormatterForFieldType(LocalDateTime.class, new LocalDateTimeFormatter());
  15. }
  16.  
  17. @Bean
  18. @Primary
  19. public ObjectMapper ObjectMapper() {
  20. String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
  21. String dateFormat = "yyyy-MM-dd";
  22. String timeFormat = "HH:mm:ss";
  23. ObjectMapper objectMapper = new ObjectMapper();
  24. objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
  25. objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  26. JavaTimeModule javaTimeModule = new JavaTimeModule();
  27. // 序列化
  28. javaTimeModule.addSerializer(
  29. LocalDateTime.class,
  30. new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
  31. javaTimeModule.addSerializer(
  32. LocalDate.class,
  33. new LocalDateSerializer(DateTimeFormatter.ofPattern(dateFormat)));
  34. javaTimeModule.addSerializer(
  35. LocalTime.class,
  36. new LocalTimeSerializer(DateTimeFormatter.ofPattern(timeFormat)));
  37. javaTimeModule.addSerializer(
  38. Date.class,
  39. new DateSerializer(false, new SimpleDateFormat(dateTimeFormat)));
  40.  
  41. // 反序列化
  42. javaTimeModule.addDeserializer(
  43. LocalDateTime.class,
  44. new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
  45. javaTimeModule.addDeserializer(
  46. LocalDate.class,
  47. new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
  48. javaTimeModule.addDeserializer(
  49. LocalTime.class,
  50. new LocalTimeDeserializer(DateTimeFormatter.ofPattern(timeFormat)));
  51. javaTimeModule.addDeserializer(Date.class, new DateDeserializers.DateDeserializer() {
  52. @SneakyThrows
  53. @Override
  54. public Date deserialize(JsonParser jsonParser, DeserializationContext dc) {
  55. String text = jsonParser.getText().trim();
  56. SimpleDateFormat sdf = new SimpleDateFormat(dateTimeFormat);
  57. return sdf.parse(text);
  58. }
  59. });
  60. javaTimeModule.addSerializer(Long.class, ToStringSerializer.instance);
  61. javaTimeModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
  62. objectMapper.registerModule(javaTimeModule);
  63. return objectMapper;
  64. }
  65. }
  1. WebMvcConfigurationSupport类

编写Jackson配置

重新编写Jackson后,并不能处理get请求时,日期等特殊对象的序列化处理;针对get请求,编写对象的序列化规则函数,通过实现addFormatters()接口,可扩展支持;
编写自定义配置Jackson信息时,需要重写extendMessageConverters方法。具体技术细节原因,请参考文档《Spring Boot实现WebMvcConfigurationSupport导致自定义的JSON时间返回格式不生效》

  1. /**
  2. * 项目全局配置类
  3. *
  4. * @author wangling
  5. * @date 2022/06/10
  6. */
  7. @Configuration
  8. public class MvcInterceptorConfig extends WebMvcConfigurationSupport {
  9.  
  10. @Override
  11. protected void addFormatters(FormatterRegistry registry) {
  12. // 用于get 全局格式化日期转换
  13. registry.addFormatterForFieldType(LocalDate.class, new LocalDateFormatter());
  14. registry.addFormatterForFieldType(LocalDateTime.class, new LocalDateTimeFormatter());
  15. }
  16.  
  17. @Override
  18. protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
  19. // 代替框架默认的JackSon配置 用于post 全局格式化日期转换,long转字符串
  20. MappingJackson2HttpMessageConverter jackson2HttpMessageConverter =
  21. new MappingJackson2HttpMessageConverter();
  22. jackson2HttpMessageConverter.setObjectMapper(ObjectMapper());
  23. // 基于顺序,先执行自定义的
  24. converters.add(0, jackson2HttpMessageConverter);
  25. }
  26.  
  27. private ObjectMapper ObjectMapper() {
  28. String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
  29. String dateFormat = "yyyy-MM-dd";
  30. String timeFormat = "HH:mm:ss";
  31. ObjectMapper objectMapper = new ObjectMapper();
  32. //忽略空Bean转json的错误
  33. objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
  34. //忽略 在json字符串中存在,但是在对象中不存在对应属性的情况,防止错误。
  35. // 例如json数据中多出字段,而对象中没有此字段。如果设置true,抛出异常,因为字段不对应;false则忽略多出的字段,默认值为null,将其他字段反序列化成功
  36. objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  37. JavaTimeModule javaTimeModule = new JavaTimeModule();
  38. // 序列化
  39. javaTimeModule.addSerializer(
  40. LocalDateTime.class,
  41. new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
  42. javaTimeModule.addSerializer(
  43. LocalDate.class,
  44. new LocalDateSerializer(DateTimeFormatter.ofPattern(dateFormat)));
  45. javaTimeModule.addSerializer(
  46. LocalTime.class,
  47. new LocalTimeSerializer(DateTimeFormatter.ofPattern(timeFormat)));
  48. javaTimeModule.addSerializer(
  49. Date.class,
  50. new DateSerializer(false, new SimpleDateFormat(dateTimeFormat)));
  51.  
  52. // 反序列化
  53. javaTimeModule.addDeserializer(
  54. LocalDateTime.class,
  55. new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateTimeFormat)));
  56. javaTimeModule.addDeserializer(
  57. LocalDate.class,
  58. new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
  59. javaTimeModule.addDeserializer(
  60. LocalTime.class,
  61. new LocalTimeDeserializer(DateTimeFormatter.ofPattern(timeFormat)));
  62. javaTimeModule.addDeserializer(Date.class, new DateDeserializers.DateDeserializer() {
  63. @SneakyThrows
  64. @Override
  65. public Date deserialize(JsonParser jsonParser, DeserializationContext dc) {
  66. String text = jsonParser.getText().trim();
  67. SimpleDateFormat sdf = new SimpleDateFormat(dateTimeFormat);
  68. return sdf.parse(text);
  69. }
  70. });
  71. javaTimeModule.addSerializer(Long.class, ToStringSerializer.instance);
  72. javaTimeModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
  73. objectMapper.registerModule(javaTimeModule);
  74. return objectMapper;
  75. }
  76. }

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数据类型;
 

一文详解JackSon配置信息的更多相关文章

  1. 一文详解Hexo+Github小白建站

    作者:玩世不恭的Coder时间:2020-03-08说明:本文为原创文章,未经允许不可转载,转载前请联系作者 一文详解Hexo+Github小白建站 前言 GitHub是一个面向开源及私有软件项目的托 ...

  2. log4j.properties 详解与配置步骤(转)

    找的文章,供参考使用 转自 log4j.properties 详解与配置步骤 一.log4j.properties 的使用详解 1.输出级别的种类 ERROR.WARN.INFO.DEBUGERROR ...

  3. C3P0连接池详解及配置

    C3P0连接池详解及配置 本人使用的C3P0的jar包是:c3p0-0.9.1.jar <bean id = "dataSource" class = "com.m ...

  4. rsync的介绍及参数详解,配置步骤,工作模式介绍

    rsync的介绍及参数详解,配置步骤,工作模式介绍 rsync是类unix系统下的数据镜像备份工具.它是快速增量备份.全量备份工具. Sync可以远程同步,支持本地复制,或者与其他SSH.rsync主 ...

  5. 磁盘分区对齐详解与配置 – Linux篇

    在之前一篇<磁盘分区对齐详解与配置 – Windows篇>中,我介绍了磁盘分区对齐的作用和适用于MBR和GPT的两种磁盘类型的配置,以及Windows平台设置磁盘分区对齐的方法. 本文作为 ...

  6. tomcat启动nio,apr详解以及配置

    tomcat启动nio,apr详解以及配置 前言 在正文开始之前,我们先在idea工具中看看启动的信息,顺便看下启动的基本信息 在这里插入图片描述可以看到信息有tomcat版本操作系统版本java版本 ...

  7. 一文详解 Linux 系统常用监控工一文详解 Linux 系统常用监控工具(top,htop,iotop,iftop)具(top,htop,iotop,iftop)

    一文详解 Linux 系统常用监控工具(top,htop,iotop,iftop)     概 述 本文主要记录一下 Linux 系统上一些常用的系统监控工具,非常好用.正所谓磨刀不误砍柴工,花点时间 ...

  8. nginx的gzip模块详解以及配置

    文章来源 运维公会:nginx的gzip模块详解以及配置   1.gzip模块作用 gzip这个模块无论在测试环境还是生产环境都是必须要开启,这个模块能高效的将页面的内容,无论是html或者css.j ...

  9. SpringBoot Profile使用详解及配置源码解析

    在实践的过程中我们经常会遇到不同的环境需要不同配置文件的情况,如果每换一个环境重新修改配置文件或重新打包一次会比较麻烦,Spring Boot为此提供了Profile配置来解决此问题. Profile ...

随机推荐

  1. 踩了个DNS解析的坑,但我还是没想通

    hello大家好,我是小楼. 最近踩了个DNS解析的小坑,虽然问题解决了,但排查过程比较曲折,最后还是有一点没有想通,整个过程分享给大家. 背景 最近负责的服务要置换机器.置换机器可能很多小伙伴不知道 ...

  2. SSM整合_年轻人的第一个增删改查_基础环境搭建

    写在前面 SSM整合_年轻人的第一个增删改查_基础环境搭建 SSM整合_年轻人的第一个增删改查_查找 SSM整合_年轻人的第一个增删改查_新增 SSM整合_年轻人的第一个增删改查_修改 SSM整合_年 ...

  3. JavaWeb和WebGIS学习笔记(七)——MapGuide Open Source安装、配置以及MapGuide Maestro发布地图——超详细!目前最保姆级的MapGuide上手指南!

    JavaWeb和WebGIS学习笔记(七)--MapGuide Open Source安装.配置以及MapGuide Maestro发布地图 超详细!目前最保姆级的MapGuide上手指南! 系列链接 ...

  4. 【SpringBoot实战】实现WEB的常用功能

    前言 通常在 Web 开发中,会涉及静态资源的访问支持.视图解析器的配置.转换器和格式化器的定制.文件上传下载等功能,甚至还需要考虑到与Web服务器关联的 Servlet相关组件的定制.Spring ...

  5. C# 一维数组如何快速实现数组元素的数据类型的转换?

    一.场景假设 假设有一串字符串如下所示,字符串中的数字之间已用英文状态下的逗号隔开.要求用此字符串中的数字快速生成int类型的数组,且尽可能使用最少的代码量. string str = "1 ...

  6. 为什么建议大家使用 Linux 开发?

    关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ Linux 能用吗? 我身边还有些朋友对 linux 的印象似乎还停留在黑乎乎的命令行界面上. ...

  7. dotnet 6 在 Win7 系统证书链错误导致 HttpWebRequest 内存泄露

    本文记录我将应用迁移到 dotnet 6 之后,在 Win7 系统上,因为使用 HttpWebRequest 访问一个本地服务,此本地服务开启 https 且证书链在此 Win7 系统上错误,导致应用 ...

  8. 第一个Python程序 | 机选彩票号码+爬取最新开奖号码

    (机选彩票号码+爬取最新开奖号码 | 2021-04-21) 学习记录,好记不如烂笔头 这个程序作用是<机选三种彩票类型的号码> 程序内包含功能有如下: 自动获取最新的三种彩票的开奖号码 ...

  9. CentOS删除编译安装的Python3

    编译安装Python3 # 下载 # wget https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tar.xz wget http://mirr ...

  10. 什么是请求参数、表单参数、url参数、header参数、Cookie参数?一文讲懂

    最近在工作中对 http 的请求参数解析有了进一步的认识,写个小短文记录一下. 回顾下自己的情况,大概就是:有点点网络及编程基础,只需要加深一点点对 HTTP 协议的理解就能弄明白了. 先分享一个小故 ...