一步到位 SpringBoot 序列化与消息转换器 (你需要的这里都有)

简介: 本篇文章记录的为SpringBoot Jackson序列化,ObjectMapper,configureMessageConverters,MappingJackson2HttpMessageConverter消息转换器相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬

👳我亲爱的各位大佬们好

♨️本篇文章记录的为SpringBoot Jackson序列化,ObjectMapper,configureMessageConverters, MappingJackson2HttpMessageConverter消息转换器相关内容,适合在学Java的小白,帮助新手快速上手,也适合复习中,面试中的大佬🙉🙉🙉。
♨️如果文章有什么需要改进的地方还请大佬不吝赐教❤️🧡💛
👨‍🔧 个人主页 : 阿千弟

@[toc]

序列化与反序列化

1、认识序列化与反序列化

Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程。

2、为什么要实现对象的序列化和反序列化?

(1)我们创建的Java对象被存储在Java堆中,当程序运行结束后,这些对象会被JVM回收。但在现实的应用中,可能会要求在程序运行结束之后还能读取这些对象,并在以后检索数据,这时就需要用到序列化。

(2)当Java对象通过网络进行传输的时候。因为数据只能够以二进制的形式在网络中进行传输,因此当把对象通过网络发送出去之前需要先序列化成二进制数据,在接收端读到二进制数据之后反序列化成Java对象。

在这里插入图片描述

Jackson之ObjectMapper对象的使用

1. 引入jackson:

搭建springMvc框架时,遇到一个Controller接收实体参数问题:method为post,content-type为application/json; charset=utf-8的http请求,Controller接收是@RequestBody ,但无法序列化为Object类型,其它类型却可以,并且报415错误Unsupported Media Type,可以排除注解未开启、请求内容类型不一致问题,那么最有可能是jackson包没引用或者与spring版本号不一致。

Spring5.x引用jackson 2.9版本,Spring4.x引用jackson 2.6,Spring3.x引用jackson 1.9版本都是可以的。

<!--jackson-->
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <!-- 注意版本应该与 Springboot版本匹配。看SpringBoot版本发布日期去Maven找对应的版本号 -->
</dependency>

这里我用到springboot版本是2.5.4, 所对应的版本jackson版本是2.12.4

2. 介绍

Jackson ObjectMapper类(com.fasterxml.jackson.databind.ObjectMapper)是使用Jackson解析JSON最简单的方法。Jackson ObjectMapper可以从字符串、流或文件解析JSON,并创建Java对象或对象图来表示已解析的JSON。将JSON解析为Java对象也称为从JSON反序列化Java对象
Jackson ObjectMapper也可以从Java对象创建JSON. 从Java对象生成JSON的过程也被称为序列化Java对象到JSON
Jackson对象映射器(Object Mapper)可以把JSON解析为用户自定义类对象, 或者解析为JSON内置的树模型的对象

3.Jackson 注解

@JsonProperty

@JsonProperty 注解指定一个属性用于 JSON 映射,默认情况下映射的 JSON 属性与注解的属性名称相同,不过可以使用该注解的 value 值修改 JSON 属性名,该注解还有一个 index 属性指定生成 JSON 属性的顺序,如果有必要的话。

@JsonIgnore

@JsonIgnore 注解用于排除某个属性,这样该属性就不会被 Jackson 序列化和反序列化。

@JsonIgnoreProperties

@JsonIgnoreProperties 注解是类注解。在序列化为 JSON 的时候,@JsonIgnoreProperties({"prop1", "prop2"}) 会忽略 pro1 和 pro2 两个属性。在从 JSON 反序列化为 Java 类的时候,@JsonIgnoreProperties(ignoreUnknown=true) 会忽略所有没有 Getter 和 Setter 的属性。该注解在 Java 类和 JSON 不完全匹配的时候很有用。

@JsonIgnoreType

@JsonIgnoreType 也是类注解,会排除所有指定类型的属性。

@JsonPropertyOrder

@JsonPropertyOrder 和 @JsonProperty的 index 属性类似,指定属性序列化时的顺序。

@JsonRootName

@JsonRootName 注解用于指定 JSON 根属性的名称。

@JsonInclude

@JsonInclude(JsonInclude.Include.NON_NULL):对值为 null 的属性不进行序列化
@JsonInclude(JsonInclude.Include.NON_EMPTY) : 表示在序列化时,将值为null的字段排除掉。

@JsonFormat

添加到需要指定格式的日期属性上,指定日期属性序列化与反序列化时的格式。timezone = “GMT+8” 设置时区,表示 +8 小时,否则会少8小时。ObjectMapper 序列化 POJO 对象为 json 字符串时,Date 日期类型默认会转为 long 长整型,json 字符串反序列化为 POJO 时自动将长整型的日期转为 Date 类型。

4.ObjectMapper常用API

Java 对象与 Json 字符串的转换

String writeValueAsString(Object value) | 1、用于将任何 Java 对象(如 POJO、List、Set、Map等)序列化为 json 字符串,如果对象中某个属性的值为 null,则默认也会序列化为 null;2、如果 value 为 null,返回序列化的结果也返回 null
byte[] writeValueAsBytes(Object value) | 将 java 对象序列化为 字节数组
writeValue(File resultFile, Object value) | 将 java 对象序列化并输出指定文件中
writeValue(OutputStream out, Object value)| 将 java 对象序列化并输出到指定字节输出流中
writeValue(Writer w, Object value) | 将 java 对象序列化并输出到指定字符输出流中
T readValue(String content, Class valueType) | 1、从给定的 JSON 字符串反序列化为 Java 对象;2、content 为空或者为 null,都会报错3、valueType 表示反序列化的结果对象,可以是任何 java 对象,比如 POJO、List、Set、Map 等等.
T readValue(byte[] src, Class valueType) | 将 json 内容的字节数组反序列化为 java 对象
T readValue(File src, Class valueType) | 将本地 json 内容的文件反序列化为 java 对象
T readValue(InputStream src, Class valueType) | 将 json 内容的字节输入流反序列化为 java 对象
T readValue(Reader src, Class valueType) | 将 json 内容的字符输入流反序列化为 java 对象
T readValue(URL src, Class valueType) | 通过网络 url 地址将 json 内容反序列化为 java 对象

5.配置ObjectMapper

ObjectMapper objectMapper = new ObjectMapper();
//去掉默认的时间戳格式     
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

//设置为东八区
objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));

// 设置输入:禁止把POJO中值为null的字段映射到json字符串中
objectMapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);

//空值不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

//反序列化时,属性不存在的兼容处理
objectMapper.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

//序列化时,日期的统一格式
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

//序列化日期时以timestamps输出,默认true
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

//序列化枚举是以toString()来输出,默认false,即默认以name()来输出
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING,true);

//序列化枚举是以ordinal()来输出,默认false
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_INDEX,false);

//类为空时,不要抛异常
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

//反序列化时,遇到未知属性时是否引起结果失败
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

//单引号处理
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);

//解析器支持解析结束符
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);

5.1 反序列化时陈列多余字段

在默认情况下,如果Json数据中有多余的字段,那么在反序列化时Jackson发现无法找到对应的对象字段,便会抛出UnrecognizedPropertyException: Unrecognized field xxx异常,此时可以做如下配置:

objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

5.2 设置输入:禁止把POJO中值为null的字段映射到json字符串中

旧的配置方法(在2.9版本后已经过时)

在这里插入图片描述
在这里插入图片描述

新的配置方法

this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

5.3 long类型精度丢失解决

原因 : 前端js对Long类型支持的精度不够,导致后端使用的Long传到前端丢失精度,比如现在分布式id生成算法“雪花算法”在使用中就会出现问题

解决方案:

  1. 在Long类型字段上使用注解标明序列化方式,代码量不大的情况可以考虑@JsonSerialize(using = ToStringSerializer.class)private Long id;

  2. 配置以下代码

SimpleModule simpleModule= newSimpleModule();

simpleModule.addSerializer(BigInteger.class, ToStringSerializer.instance);
//暂时放弃对小long的转换,约定与前端交互数据时,大Long全部转换成字符串
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);

simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
//注册功能模块 例如,可以添加自定义序列化器和反序列化器
this.registerModule(simpleModule);
// 对象和json互相转化的过程当中按照此转化方式转哈
@JsonFormat(
            pattern = "yyyy年MM月dd日",
            timezone = "GMT-8"
    )
private Date birthday;
// 从requestParam中获取参数并且转化
@DateTimeFormat(pattern = "yyyy年MM月dd日")
private Date birthday;
@Configuration
public class CustomObjectMapper extends ObjectMapper {
   
   

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public CustomObjectMapper() {
   
   
        super();
        //去掉默认的时间戳格式
        this.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //设置为东八区
        this.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // 设置输入:禁止把POJO中值为null的字段映射到json字符串中
        //this.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
        this.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        // 空值不序列化
        this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 序列化枚举是以toString()来输出,默认false,即默认以name()来输出
        this.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);

        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

添加自定义的消息转换器

我的项目中mvc配置是直接实现的WebMvcConfigurer,这两里面的方法一样,不同的是WebMvcConfigurationSupport里面有很多springboot为我们做的配置,而WebMvcConfigurer里面是空的;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
   
   

//    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    // 这个方法是用来配置静态资源的,比如html,js,css,等等
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry){
   
          
    }

    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
   
       
    }

    //拓展mvc框架的消息转换器
    @Autowired
    private CustomObjectMapper customObjectMapper;
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
   
   

        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置消息转换器,底层使用jackson将Java对象转成json
        messageConverter.setObjectMapper(customObjectMapper);
        //将我们自定义的消息转换器追加到mvc框架的集合转换器中
        converters.add(0, messageConverter);
    }
}

添加信息转换器的几种方式:

1.注解Bean形式:

// 这样做springboot会把我们自定义的converter放在顺序上的最高优先级(List的头部)
// 即有多个converter都满足Accpet/ContentType/MediaType的规则时,优先使用我们这个
@Bean
public MappingJackson2HttpMessageConverter  mappingJackson2HttpMessageConverter(){
   
   
        return new MappingJackson2HttpMessageConverter();
}

2.springboot通过继承WebMvcConfigurerAdapter,重写configureMessageConverters。
(Spring3.x 用MappingJacksonHttpMessageConverter
Spring4.x 用MappingJackson2HttpMessageConverter)

// 通常在只有一个自定义WebMvcConfigurerAdapter时,会把这个方法里面添加的converter(s)依次放在最高优先级(List的头部)
// 虽然第一种方式的代码先执行,但是bean的添加比这种方式晚,所以方式二的优先级 大于 方式一
@Configuration
@EnableWebMvc
public class WebMvcConfigure extends WebMvcConfigurerAdapter {
   
   

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters){
   
   
        MappingJackson2HttpMessageConverter mjhmc=new MappingJackson2HttpMessageConverter(objectMapper());
        converters.add(mjhmc);
    }

    @Bean
    public ObjectMapper objectMapper(){
   
   
        ObjectMapper om= Jackson2ObjectMapperBuilder.json().build();
        om.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE,ToStringSerializer.instance);
        simpleModule.addSerializer(SwordsAbstractModel.class,new CustomerJpaModelSerializer());
        om.registerModule(simpleModule);
        return om;
    }
}

3.extendMessageConverters方式:

// 添加converter的第三种方式
// 同一个WebMvcConfigurerAdapter中的configureMessageConverters方法先于extendMessageConverters方法执行
// 可以理解为是三种方式中最后执行的一种,不过这里可以通过add指定顺序来调整优先级,也可以使用remove/clear来删除converter,功能强大
// 使用converters.add(xxx)会放在最低优先级(List的尾部)
// 使用converters.add(0,xxx)会放在最高优先级(List的头部)
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
   
   
        converters.add(new MappingJackson2HttpMessageConverter());
}

dadadad.gif

消息转换器configureMessageConverters和extendMessageConverters区别

在spring中配置WebMvc时有两种方法,一种是继承WebMvcConfigurationSupport,重写里面相应的方法,还有一种是继承WebMvcConfigurer的子抽象类WebMvcConfigurerAdapter,也是重写里面相应的方法,但是需要在配置类上添加@EnableWebMvc注解。那这两个类直接是什么关系呢?

configureMessageConverters

配置HttpMessageConverters以用于读取或写入请求或响应的主体。如果未添加转换器,则会注册转换器的默认列表。请注意,向列表中添加转换器将关闭默认转换器注册。只需添加一个转换器而不影响默认注册,请考虑使用extendMessageConverters

extendMessageConverters

用于在配置转换器后扩展或修改转换器列表的钩子。这可能很有用,例如允许注册默认转换器,然后通过此方法插入自定义转换器。

总结

添加自定义消息转换器不覆盖默认转换器请使用extendMessageConverters
添加自定义会覆盖默认转换器使用configureMessageConverters
006HJgYYgy1ft42gwg5hog306y06yjwa.gif

如果这篇【文章】有帮助到你💖,希望可以给我点个赞👍,创作不易,如果有对Java后端或者对spring感兴趣的朋友,请多多关注💖💖💖
👨‍🔧 个人主页 : 阿千弟

目录
相关文章
|
4月前
|
消息中间件 JSON 缓存
RabbitMQ快速学习之WorkQueues模型、三种交换机、消息转换器(SpringBoot整合)
RabbitMQ快速学习之WorkQueues模型、三种交换机、消息转换器(SpringBoot整合)
|
1月前
|
JSON 安全 Java
Spring Boot 序列化、反序列化
本文介绍了Spring Boot中的序列化和反序列化。Java提供默认序列化机制,通过实现Serializable接口实现对象到字节流的转换。Spring Boot默认使用Jackson处理JSON,可通过注解和配置自定义规则。然而,序列化可能引发安全问题,建议使用白名单、数据校验和安全库。最佳实践包括使用标准机制、自定义规则及注意版本控制。文章还提醒关注性能并提供了相关参考资料。
47 2
|
4月前
|
消息中间件 JSON 缓存
RabbitMQ快速学习之WorkQueues模型、三种交换机、消息转换器(基于SpringBoot)
RabbitMQ快速学习之WorkQueues模型、三种交换机、消息转换器(基于SpringBoot)
|
5月前
|
存储 缓存 NoSQL
SpringBoot整合Redis调用lua脚本出现空指针异常(序列化器问题)
一、问题描述 业务中出现需要保证原子性的一系列缓存操作,所以决定使用lua脚本来保证原子性。 但是调用过程中lua脚本抛出了异常:attempt to perform arithmetic on local ‘xxx’ (a nil value) 发生异常的lua脚本代码(部分)
|
8月前
|
缓存 NoSQL Java
SpringBoot自定义redisTemplate的key和value的序列化方式
SpringBoot自定义redisTemplate的key和value的序列化方式
106 0
|
9月前
|
存储 缓存 NoSQL
SpringBoot整合Redis调用lua脚本出现空指针异常(序列化器问题)
一、问题描述 业务中出现需要保证原子性的一系列缓存操作,所以决定使用lua脚本来保证原子性。 但是调用过程中lua脚本抛出了异常:attempt to perform arithmetic on local ‘xxx’ (a nil value) 发生异常的lua脚本代码(部分)
|
11月前
|
JSON 前端开发 Java
【工作中问题解决实践 七】SpringBoot集成Jackson进行对象序列化和反序列化
【工作中问题解决实践 七】SpringBoot集成Jackson进行对象序列化和反序列化
265 0
|
12月前
|
消息中间件 Java Spring
八、SpringBoot整合RabbitMQ 之 SpringAMQP 消息转换器
八、SpringBoot整合RabbitMQ 之 SpringAMQP 消息转换器
|
SQL JSON 前端开发
基于Springboot外卖系统08:员工账号状态管理功能+对象转换器+扩展Spring mvc的消息转换器
在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工可以正常登录。如果某个员工账号状态为正常,则按钮显示为 "禁用",如果员工账号状态为已禁用,则按钮显示为"启用"。
118 0
|
JSON 前端开发 JavaScript
【SpringBoot+MyBatisPlus】分页的实现以及使用数值转换器来解决long型id精度丢失问题
分页的实现以及使用数值转换器来解决long型id精度丢失问题
312 0
【SpringBoot+MyBatisPlus】分页的实现以及使用数值转换器来解决long型id精度丢失问题