• springBoot项目 ObjectMapper 序列化统一格式处理


    1. 开篇 fastjson 与 jackson

    1.1 关于 fastjson 与 jackson 的简单使用

    1.2 关于 fastjson 序列化的全局控制

    • 非全局控制的,参考上面链接即可实现,在此不做介绍

    1.2.1 配置实现代码

    • 需要注意点:
      创建实现了 WebMvcConfigurer 接口的配置类ExternalFastJsonConfig.java,如果项目中已经有实现 WebMvcConfigurer 的接口类,可以直接把ExternalFastJsonConfig.java里的核心代码放进去即可,不用再创建
    • 如下:
      ExternalFastJsonConfig.java
      package com.liu.susu.common.config;
      
      import com.alibaba.fastjson.serializer.SerializerFeature;
      import com.alibaba.fastjson.serializer.ValueFilter;
      import com.alibaba.fastjson.support.config.FastJsonConfig;
      import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
      import com.liu.susu.common.util.DateUtils;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.http.MediaType;
      import org.springframework.http.converter.HttpMessageConverter;
      import org.springframework.web.servlet.config.annotation.*;
      
      import java.nio.charset.Charset;
      import java.time.LocalDate;
      import java.time.LocalDateTime;
      import java.util.ArrayList;
      import java.util.Date;
      import java.util.List;
      
      /**
       * description 统一处理返回前端的json格式
       * @author susu
       **/
      @Configuration
      public class ExternalFastJsonConfig implements WebMvcConfigurer {
      
          private static final String FORMAT = "yyyy-MM-dd HH:mm:ss";
      
          private static SerializerFeature[] serializerFeatures = new SerializerFeature[]{
                  SerializerFeature.PrettyFormat,
                  SerializerFeature.WriteEnumUsingToString,
                  SerializerFeature.IgnoreNonFieldGetter
          };
      
          private static ValueFilter filter = new ValueFilter () {
      
              public Object process(Object obj, String key, Object value) {
                  if(value==null){
      //                return "is null ???? I want to change the value!!!";
                  }else if("".equals(value.toString())){
                      return null;
                  }
                  if (value instanceof LocalDateTime){
      //                return "2022-07-29 10:43:22";
                      return DateUtils.getDateTimeStr((LocalDateTime)value);
                  }else if (value instanceof LocalDate){
                      return DateUtils.getDateStrYMD((LocalDate)value);
                  }else if (value instanceof Date){
                      return DateUtils.getDateStrYMD((Date)value);
                  }
                  return value;
              }
          };
      
          @Bean
          public HttpMessageConverter fastJsonHttpMessageConverters(){
      
              FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
              FastJsonConfig config = new FastJsonConfig();
      
              config.setSerializerFeatures(serializerFeatures);
              config.setDateFormat(FORMAT);
              config.setSerializeFilters(filter);
              fastConverter.setFastJsonConfig(config);
              fastConverter.setDefaultCharset(Charset.forName("UTF-8"));
              List<MediaType> mediaTypeList = new ArrayList<>();
              // 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属性produces = "application/json"
              mediaTypeList.add(MediaType.APPLICATION_JSON);
              fastConverter.setSupportedMediaTypes(mediaTypeList);
              HttpMessageConverter<?> converter=fastConverter;
              return converter;
          }
      
          @Override
          public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
      //        converters.add(fastJsonHttpMessageConverters());
          }
      
          //解决跨域问题
          @Override
          public void addCorsMappings(CorsRegistry registry) {
              registry.addMapping("/**")
                      .allowedOrigins("*")
                      .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                      .allowCredentials(true)
                      .maxAge(3600)
                      .allowedHeaders("*");
          }
      
          @Override
          public void addResourceHandlers(ResourceHandlerRegistry registry) {
      //        registry.addResourceHandler("/**").addResourceLocations(
      //                "classpath:/static/");
      //        registry.addResourceHandler("/doc.html").addResourceLocations(
      //                "classpath:/META-INF/resources/");
      //        registry.addResourceHandler("/webjars/**").addResourceLocations(
      //                "classpath:/META-INF/resources/webjars/");
              registry.addResourceHandler("/**").addResourceLocations("/");
      //        super.addResourceHandlers(registry);
          }
      
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104

    DateUtils.java

    • 这个简单了,自己根据需要定义即可,因为我上面用到了不用的Date类型,所以在 ValueFilter 里分别处理了一下
    • 代码如下:
      package com.liu.susu.common.util;
      
      import java.time.LocalDate;
      import java.time.LocalDateTime;
      import java.time.ZoneId;
      import java.time.format.DateTimeFormatter;
      import java.util.Date;
      
      /**
       * @FileName DateUtils
       * @Description
       * @Author susu
       **/
      public class DateUtils {
      
          private static final String FORMAT_1 = "yyyy-MM-dd HH:mm:ss";
          private static final String FORMAT_2 = "yyyy-MM-dd";
      
      
          /**
           * description :把 LocalDate 类型转换成 "yyyy-MM-dd"
           * @param localDate
           * @return java.lang.String
           * @author susu
           */
          public static String getDateStrYMD(LocalDate localDate){
              DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(FORMAT_2);
              return dateTimeFormatter.format(localDate);
          }
      
          /**
           * description :如果有时分秒直接返回(时分秒是0的),如果没有返回年月日
           * @param date
           * @return java.lang.String
           * @author susu
           */
          public static String getDateStrYMD(Date date){
              LocalDateTime localDateTime = date.toInstant().atZone((ZoneId.systemDefault())).toLocalDateTime();
              if (localDateTime.getHour()==0 && localDateTime.getSecond()==0 && localDateTime.getMinute()==0){
                  return getDateYMD(localDateTime);
              }
              return getDateTimeStr(localDateTime);
          }
      
          /**
           * description :把 LocalDateTime 类型转换成 "yyyy-MM-dd"
           * @param localDateTime
           * @return java.lang.String
           * @author susu
           */
          public static String getDateYMD(LocalDateTime localDateTime){
              DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(FORMAT_2);
              return dateTimeFormatter.format(localDateTime);
          }
      
          /**
           * description :把 LocalDateTime 类型转换成 "yyyy-MM-dd HH:mm:ss"
           * @param localDateTime
           * @return java.lang.String
           * @author susu
           */
          public static String getDateTimeStr(LocalDateTime localDateTime){
              DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(FORMAT_1);
              return dateTimeFormatter.format(localDateTime);
          }
      
      
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69

    1.2.2 存在的问题

    • 对于枚举的序列化不是很好用,我这边没有实现,如果有更好的方法,欢迎沟通交流

    • 枚举序列化 MyEnumSerializer .java

      package com.liu.susu.common;
      
      import com.fasterxml.jackson.core.JsonGenerator;
      import com.fasterxml.jackson.databind.JsonSerializer;
      import com.fasterxml.jackson.databind.SerializerProvider;
      
      import java.io.IOException;
      
      
      /**
       * description
       * @author susu
       * @date 2022-08-12
       **/
      public class MyEnumSerializer extends JsonSerializer<MyEnum> {
      
          @Override
          public void serialize(MyEnum value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
      
              gen.writeStartObject();
              gen.writeFieldName("value");
              gen.writeString(value.getValue().toString());
              gen.writeFieldName("name");
              gen.writeString(value.getName());
              gen.writeEndObject();
      
          }
      }
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • MyEnum.java
      public interface MyEnum<E extends Enum<?>, T>  {
          Object getValue();
          String getName();
      }
      
      • 1
      • 2
      • 3
      • 4
    • 使用方式
      方式1:在实体的属性上加注解:

      @JSONField(serializeUsing = MyEnumSerializer.class)
      
      • 1

      方式2:在 MyEnum 上加注解:

      @JSONType(serializer = MyEnumSerializer.class)
      
      • 1

      但是,验证试了一下,这两种方式都没有实现,具体怎么实现,或者是枚举序列化类有问题?我没再试,直接换了jackson,大家也可以参考下面一篇文章试试
      fastjson枚举转换(序列化、反序列化)最新版本爬坑记录.

    1.3 关于 jackson 序列化的全局控制

    1.3.1 配置类代码

    • ExternalJacksonConfig.java
      package com.liu.susu.common.config;
      
      import com.fasterxml.jackson.core.JsonGenerator;
      import com.fasterxml.jackson.core.JsonParser;
      import com.fasterxml.jackson.databind.*;
      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 org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
      import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.context.annotation.Primary;
      import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
      
      import java.io.IOException;
      import java.text.SimpleDateFormat;
      import java.time.LocalDate;
      import java.time.LocalDateTime;
      import java.time.LocalTime;
      import java.time.format.DateTimeFormatter;
      import java.util.HashMap;
      import java.util.Map;
      
      /**
       * description 统一处理返回前端的json格式
       * @author susu
       **/
      @Configuration
      public class ExternalJacksonConfig {
      
          private static final String FORMAT = "yyyy-MM-dd HH:mm:ss";
          private static final DateTimeFormatter FORMAT_YMD_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
          private static final DateTimeFormatter FORMAT_YMD = DateTimeFormatter.ofPattern("yyyy-MM-dd");
          private static final DateTimeFormatter FORMAT_TIME = DateTimeFormatter.ofPattern("HH:mm:ss");
      
          @Bean
          @Primary
          @ConditionalOnMissingBean(ObjectMapper.class)
          public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
              ObjectMapper objectMapper = builder.createXmlMapper(false).build();
              objectMapper.setDateFormat(new SimpleDateFormat(FORMAT));
              objectMapper.configure(MapperFeature.AUTO_DETECT_IS_GETTERS,false);
              objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);
              //反序列化时
              objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);//允许出现特殊字符和转义符、
              objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);//允许出现单引号
      
      
              objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
                  @Override
                  public void serialize(Object object, JsonGenerator value, SerializerProvider serializerProvider) throws IOException {
                      value.writeString("");
                  }
              });
      
              return objectMapper;
          }
      
      
          /**
           * 处理 LocalDateTime LocalDate 的序列化和反序列化
           * @return
           */
          @Bean
          public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
              Map<Class<?>, JsonSerializer<?>> serializers = new HashMap<>();
              serializers.put(LocalDateTime.class, new LocalDateTimeSerializer(FORMAT_YMD_TIME));
              serializers.put(LocalDate.class, new LocalDateSerializer(FORMAT_YMD));
              serializers.put(LocalTime.class, new LocalTimeSerializer(FORMAT_TIME));
      
              Map<Class<?>, JsonDeserializer<?>> deserializers = new HashMap<>();
              deserializers.put(LocalDateTime.class, new LocalDateTimeDeserializer(FORMAT_YMD_TIME));
              deserializers.put(LocalDate.class, new LocalDateDeserializer(FORMAT_YMD));
              deserializers.put(LocalTime.class, new LocalTimeDeserializer(FORMAT_TIME));
              return builder -> builder.serializersByType(serializers).deserializersByType(deserializers);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81

    1.3.2 注意点

    1. 枚举方面

    • 如果序列化成对象枚举,需要加注解
      @JsonFormat(shape = JsonFormat.Shape.OBJECT)
      或者
      @JsonSerialize(using = MyEnumSerializer.class)
      在这里插入图片描述

    2. LocalDateTime LocalDate 的序列化和反序列化处理

    • 直接截图,上面代码有,如下:
      在这里插入图片描述
      在这里插入图片描述

    1.3.3 单个序列化怎么处理 LocalDateTime?

    • 参考下面的一篇文章,可以解决
      Jackson在springboot中LocalDateTime格式问题
    • 代码如下:
      ObjectMapper objectMapper = new ObjectMapper();
      
              objectMapper.setDateFormat(new SimpleDateFormat(FORMAT));
              JavaTimeModule javaTimeModule = new JavaTimeModule();
              javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
              javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
              javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
              javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
              javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
              javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
              objectMapper.registerModule(javaTimeModule);
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11

    2.

  • 相关阅读:
    【面试经典150 | 数组】加油站
    开源办公套件LibreOffice
    LeetCode220726_50、填充每个节点的下一个右侧节点指针
    C# 开源SDK 工业相机库 调用海康相机 大恒相机
    推免复习(一):数据库复习提纲
    Iceberg Flink FLIP-27实现
    信奥中的数学:加法原理和乘法原理
    MMDetection(三):公开数据集上测试和训练模型
    通信知识点集合
    企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
  • 原文地址:https://blog.csdn.net/suixinfeixiangfei/article/details/126319346