Skip to content

序列化与反序列化

说明

为了统一参数格式和数据精度丢失等问题,本项目做了采用jackson进行全局的序列化和反序列化配置。

名词解释:

  1. 序列化:Controller层接口返回值 转成 json 格式的过程
  2. 反序列化:前端请求通过json格式提交参数到 Controller 层的过程

全局配置

在BaseConfig类配置了全局的ObjectMapper 实例, 并对objectMapper进行了自定义配置。

注意

配置全局ObjectMapper类后,会和yml配置文件中 spring.jackson.xxx 的配置产生冲突,所以请勿在yml中重复配置

java
@Bean
@Primary
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnMissingBean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();

    objectMapper
     // 设置当前位置
    .setLocale(Locale.CHINA)
    //去掉默认的时间戳格式
    .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
    // 时区
    .setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()))
    //Date参数日期格式
    .setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT, Locale.CHINA))
    //该特性决定parser是否允许JSON字符串包含非引号控制字符(值小于32的ASCII字符,包含制表符和换行符)。 如果该属性关闭,则如果遇到这些字符,则会抛出异常。JSON标准说明书要求所有控制符必须使用引号,因此这是一个非标准的特性
    .configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true)
    // 忽略不能转义的字符
    .configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true)
    //在使用spring boot + jpa/hibernate,如果实体字段上加有FetchType.LAZY,并使用jackson序列化为json串时,会遇到SerializationFeature.FAIL_ON_EMPTY_BEANS异常
    .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
    //忽略未知字段
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    //单引号处理
    .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
    // 注册自定义模块
    objectMapper.registerModule(new AcuityJacksonModule())
    .findAndRegisterModules();
}

public class AcuityJacksonModule extends SimpleModule {

    public AcuityJacksonModule() {
        super();
        this.addDeserializer(LocalDateTime.class, AcuityLocalDateTimeDeserializer.INSTANCE);
        this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        this.addSerializer(Long.class, ToStringSerializer.instance);
        this.addSerializer(Long.TYPE, ToStringSerializer.instance);
        this.addSerializer(BigInteger.class, ToStringSerializer.instance);
        this.addSerializer(BigDecimal.class, ToStringSerializer.instance);
        //  4.x 版本无需枚举类序列化为{"code":xx,"desc":""} 的格式,需要回显枚举的,请在枚举上加 @Echo(api = Echo.ENUM_API) 注解 。
//        this.addSerializer(BaseEnum.class, EnumSerializer.INSTANCE);
        // 3.2.1 版本以后,覆盖官方的EnumDeserializer
//        this.addDeserializer(Enum.class, EnumDeserializer.INSTANCE);
    }

}
@Bean
@Primary
@ConditionalOnClass(ObjectMapper.class)
@ConditionalOnMissingBean
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();

    objectMapper
     // 设置当前位置
    .setLocale(Locale.CHINA)
    //去掉默认的时间戳格式
    .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
    // 时区
    .setTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()))
    //Date参数日期格式
    .setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT, Locale.CHINA))
    //该特性决定parser是否允许JSON字符串包含非引号控制字符(值小于32的ASCII字符,包含制表符和换行符)。 如果该属性关闭,则如果遇到这些字符,则会抛出异常。JSON标准说明书要求所有控制符必须使用引号,因此这是一个非标准的特性
    .configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true)
    // 忽略不能转义的字符
    .configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true)
    //在使用spring boot + jpa/hibernate,如果实体字段上加有FetchType.LAZY,并使用jackson序列化为json串时,会遇到SerializationFeature.FAIL_ON_EMPTY_BEANS异常
    .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
    //忽略未知字段
    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
    //单引号处理
    .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
    // 注册自定义模块
    objectMapper.registerModule(new AcuityJacksonModule())
    .findAndRegisterModules();
}

public class AcuityJacksonModule extends SimpleModule {

    public AcuityJacksonModule() {
        super();
        this.addDeserializer(LocalDateTime.class, AcuityLocalDateTimeDeserializer.INSTANCE);
        this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        this.addSerializer(Long.class, ToStringSerializer.instance);
        this.addSerializer(Long.TYPE, ToStringSerializer.instance);
        this.addSerializer(BigInteger.class, ToStringSerializer.instance);
        this.addSerializer(BigDecimal.class, ToStringSerializer.instance);
        //  4.x 版本无需枚举类序列化为{"code":xx,"desc":""} 的格式,需要回显枚举的,请在枚举上加 @Echo(api = Echo.ENUM_API) 注解 。
//        this.addSerializer(BaseEnum.class, EnumSerializer.INSTANCE);
        // 3.2.1 版本以后,覆盖官方的EnumDeserializer
//        this.addDeserializer(Enum.class, EnumDeserializer.INSTANCE);
    }

}

序列化自定义配置项

  1. Long -> String

    • 实现代码
    java
    public AcuityJacksonModule() {
        this.addSerializer(Long.class, ToStringSerializer.instance);
        this.addSerializer(Long.TYPE, ToStringSerializer.instance);
    }
    public AcuityJacksonModule() {
        this.addSerializer(Long.class, ToStringSerializer.instance);
        this.addSerializer(Long.TYPE, ToStringSerializer.instance);
    }
    • 最终效果 Controller层接口返回参数中,Long类型的字段,会转成字符串。
    json
    {
        "id": "29984253320102284"
    }
    {
        "id": "29984253320102284"
    }
    • 解决问题: 解决前端获取到Long类型的字段后,精度丢失问题。 (更多信息自行百度&谷歌)
  2. BigInteger -> String

    • 实现代码
    java
    public AcuityJacksonModule() {
        this.addSerializer(BigInteger.class, ToStringSerializer.instance);
    }
    public AcuityJacksonModule() {
        this.addSerializer(BigInteger.class, ToStringSerializer.instance);
    }
    • 最终效果&解决问题:同上
  3. BigDecimal -> String

    • 实现代码
    java
    public AcuityJacksonModule() {
        this.addSerializer(BigDecimal.class, ToStringSerializer.instance);
    }
    public AcuityJacksonModule() {
        this.addSerializer(BigDecimal.class, ToStringSerializer.instance);
    }
    • 最终效果&解决问题:同上
  4. Date -> String

    • 实现代码
    java
    public class BaseConfig {
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    objectMapper
    //Date参数日期格式
    .setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT, Locale.CHINA))
    // ...
    }
    }
    public class BaseConfig {
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    objectMapper
    //Date参数日期格式
    .setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT, Locale.CHINA))
    // ...
    }
    }
    • 最终效果 Controller层接口返回参数中,Date类型的字段,会转成字符串。
    json
    {
        "date": "yyyy-MM-dd HH:mm:ss"  // 这里的格式由上面的 DEFAULT_DATE_TIME_FORMAT 决定
    }
    {
        "date": "yyyy-MM-dd HH:mm:ss"  // 这里的格式由上面的 DEFAULT_DATE_TIME_FORMAT 决定
    }
    • 解决问题: 统一日期参数的统一格式
  5. LocalDateTime -> String

    • 实现代码
    java
    public AcuityJacksonModule() {
        this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
    }
    public AcuityJacksonModule() {
        this.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
    }
    • 最终效果 Controller层接口返回参数中,LocalDateTime类型的字段,会转成字符串。
    json
    {
        "createTime": "yyyy-MM-dd HH:mm:ss"
    }
    {
        "createTime": "yyyy-MM-dd HH:mm:ss"
    }
    • 解决问题: 同上
  6. LocalDate -> String

    • 实现代码
    java
    public AcuityJacksonModule() {
        this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
    }
    public AcuityJacksonModule() {
        this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
    }
    • 最终效果 Controller层接口返回参数中,LocalDate类型的字段,会转成字符串。
    json
    {
        "createTime": "yyyy-MM-dd"
    }
    {
        "createTime": "yyyy-MM-dd"
    }
    • 解决问题: 同上
  7. LocalTime -> String

    • 实现代码
    java
    public AcuityJacksonModule() {
        this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
    }
    public AcuityJacksonModule() {
        this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
    }
    • 最终效果 Controller层接口返回参数中,LocalTime类型的字段,会转成字符串。
    json
    {
        "createTime": "HH:mm:ss"
    }
    {
        "createTime": "HH:mm:ss"
    }
    • 解决问题: 同上

反序列化自定义配置项

  1. String -> Long 字符串类型的长整型数字,会转换为Long

  2. Object -> BaseEnum 符合 {"code":"xx"} 格式的对象,可以转换为BaseEnum

  3. String -> BaseEnum 符合枚举值的字符串,可以转换为BaseEnum

  4. String -> LocalDate, 支持前端传递格式:

    • yyyy-MM-dd: 如:2021-11-11
  5. String -> LocalDateTime, 支持前端传递格式:

    • yyyy-MM-dd: 如:2021-11-11
    • yyyy/MM/dd: 如:2021/11/11
    • yyyy年MM月dd日: 如:2021年11月11日
    • yyyy-MM-dd HH:mm:ss: 如:2021-11-11 11:11:11
    • yyyy/MM/dd HH:mm:ss: 如:2021/11/11 11:11:11
    • yyyy年MM月dd日HH时mm分ss秒: 如:2021年11月11日11时11分11秒
  6. String -> LocalTime

    支持前端传递格式: HH:mm:ss

欢迎使用天源云Saas快速开发系统