SpringBoot 默认json解析器详解和字段序列化自定义

前言

在我们开发项目API接口的时候,一些没有数据的字段会默认返回NULL,数字类型也会是NULL,这个时候前端希望字符串能够统一返回空字符,数字默认返回0,那我们就需要自定义json序列化处理

SpringBoot默认的json解析方案

我们知道在springboot中有默认的json解析器,Spring Boot 中默认使用的 Json 解析技术框架是 jackson。我们点开 pom.xml 中的 spring-boot-starter-web 依赖,可以看到一个 spring-boot-starter-json 依赖:

1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-json</artifactId> 4 <version>2.4.7</version> 5 <scope>compile</scope> 6 </dependency>

Spring Boot 中对依赖都做了很好的封装,可以看到很多 spring-boot-starter-xxx 系列的依赖,这是 Spring Boot 的特点之一,不需要人为去引入很多相关的依赖了,starter-xxx 系列直接都包含了所必要的依赖,所以我们再次点进去上面这个 spring-boot-starter-json 依赖,可以看到:

1 <dependency> 2 <groupId>com.fasterxml.jackson.core</groupId> 3 <artifactId>jackson-databind</artifactId> 4 <version>2.11.4</version> 5 <scope>compile</scope> 6 </dependency> 7 <dependency> 8 <groupId>com.fasterxml.jackson.datatype</groupId> 9 <artifactId>jackson-datatype-jdk8</artifactId> 10 <version>2.11.4</version> 11 <scope>compile</scope> 12 </dependency> 13 <dependency> 14 <groupId>com.fasterxml.jackson.datatype</groupId> 15 <artifactId>jackson-datatype-jsr310</artifactId> 16 <version>2.11.4</version> 17 <scope>compile</scope> 18 </dependency> 19 <dependency> 20 <groupId>com.fasterxml.jackson.module</groupId> 21 <artifactId>jackson-module-parameter-names</artifactId> 22 <version>2.11.4</version> 23 <scope>compile</scope> 24 </dependency>

我们在controller中返回json时候通过注解@ResponseBody就可以自动帮我们将服务端返回的对象序列化成json字符串,在传递json body参数时候 通过在对象参数上@RequestBody注解就可以自动帮我们将前端传过来的json字符串反序列化成java对象

这些功能都是通过HttpMessageConverter这个消息转换工具类来实现的

SpringMVC自动配置了JacksonGson的HttpMessageConverter,SpringBoot对此做了自动化配置

JacksonHttpMessageConvertersConfiguration

org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration

1 @Configuration(proxyBeanMethods = false) 2 @ConditionalOnClass(ObjectMapper.class) 3 @ConditionalOnBean(ObjectMapper.class) 4 @ConditionalOnProperty(name = HttpMessageConvertersAutoConfiguration.PREFERRED_MAPPER_PROPERTY, 5 havingValue = "jackson", matchIfMissing = true) 6 static class MappingJackson2HttpMessageConverterConfiguration { 7 8 @Bean 9 @ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class, 10 ignoredType = { 11 "org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter", 12 "org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" }) 13 MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) { 14 return new MappingJackson2HttpMessageConverter(objectMapper); 15 } 16 17 } 18

JacksonAutoConfiguration

org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration

1@Configuration(proxyBeanMethods = false) 2 @ConditionalOnClass(Jackson2ObjectMapperBuilder.class) 3 static class JacksonObjectMapperConfiguration { 4 5 @Bean 6 @Primary 7 @ConditionalOnMissingBean 8 ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { 9 return builder.createXmlMapper(false).build(); 10 } 11 12 } 13

Gson的自动化配置类

org.springframework.boot.autoconfigure.http.GsonHttpMessageConvertersConfiguration

1 @Configuration(proxyBeanMethods = false) 2 @ConditionalOnBean(Gson.class) 3 @Conditional(PreferGsonOrJacksonAndJsonbUnavailableCondition.class) 4 static class GsonHttpMessageConverterConfiguration { 5 6 @Bean 7 @ConditionalOnMissingBean 8 GsonHttpMessageConverter gsonHttpMessageConverter(Gson gson) { 9 GsonHttpMessageConverter converter = new GsonHttpMessageConverter(); 10 converter.setGson(gson); 11 return converter; 12 } 13 14 } 15

自定义SprinBoot的JSON解析

日期格式解析

默认返回的是时间戳类型格式,但是时间戳会少一天需要在数据库连接url上加上时区如:

1spring.datasource.url=jdbc:p6spy:mysql://47.100.78.146:3306/mall?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&autoReconnect=true 2
  1. 使用@JsonFormat注解自定义格式
1 @JsonFormat(pattern = "yyyy-MM-dd") 2 private Date birthday;

但是这种要对每个实体类中的日期字段都需要添加此注解不够灵活

  1. 全局添加 在配置文件中直接添加spring.jackson.date-format=yyyy-MM-dd

NULL字段不返回

  1. 在接口中如果不需要返回null字段可以使用@JsonInclude注解
1 @JsonInclude(JsonInclude.Include.NON_NULL) 2 private String title;

但是这种要对每个实体类中的字段都需要添加此注解不够灵活

  1. 全局添加 在配置文件中直接添加spring.jackson.default-property-inclusion=non_null

自定义字段序列化

自定义null字符串类型字段返回空字符NullStringJsonSerializer序列化

1public class NullStringJsonSerializer extends JsonSerializer { 2 @Override 3 public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 4 if (o == null) { 5 jsonGenerator.writeString(""); 6 } 7 } 8}

自定义null数字类型字段返回0默认值NullIntegerJsonSerializer序列化

1public class NullIntegerJsonSerializer extends JsonSerializer { 2 @Override 3 public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 4 if (o == null) { 5 jsonGenerator.writeNumber(0); 6 } 7 } 8}

自定义浮点小数类型4舍5入保留2位小数DoubleJsonSerialize序列化

1public class DoubleJsonSerialize extends JsonSerializer { 2 private DecimalFormat df = new DecimalFormat("##.00"); 3 4 @Override 5 public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 6 if (value != null) { 7 jsonGenerator.writeString(NumberUtil.roundStr(value.toString(), 2)); 8 }else{ 9 jsonGenerator.writeString("0.00"); 10 } 11 12 } 13} 14

自定义NullArrayJsonSerializer序列化

1public class NullArrayJsonSerializer extends JsonSerializer { 2 3 4 @Override 5 public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 6 if(o==null){ 7 jsonGenerator.writeStartArray(); 8 }else { 9 jsonGenerator.writeObject(o); 10 } 11 } 12}

自定义BeanSerializerModifier使用我们自己的序列化器进行bean序列化

1public class MyBeanSerializerModifier extends BeanSerializerModifier { 2 3 private JsonSerializer _nullArrayJsonSerializer = new NullArrayJsonSerializer(); 4 5 private JsonSerializer _nullStringJsonSerializer = new NullStringJsonSerializer(); 6 7 private JsonSerializer _nullIntegerJsonSerializer = new NullIntegerJsonSerializer(); 8 9 private JsonSerializer _doubleJsonSerializer = new DoubleJsonSerialize(); 10 11 @Override 12 public List changeProperties(SerializationConfig config, BeanDescription beanDesc, 13 List beanProperties) { // 循环所有的beanPropertyWriter 14 for (int i = 0; i < beanProperties.size(); i++) { 15 BeanPropertyWriter writer = (BeanPropertyWriter) beanProperties.get(i); 16 // 判断字段的类型,如果是array,list,set则注册nullSerializer 17 if (isArrayType(writer)) { //给writer注册一个自己的nullSerializer 18 writer.assignNullSerializer(this.defaultNullArrayJsonSerializer()); 19 } 20 if (isStringType(writer)) { 21 writer.assignNullSerializer(this.defaultNullStringJsonSerializer()); 22 } 23 if (isIntegerType(writer)) { 24 writer.assignNullSerializer(this.defaultNullIntegerJsonSerializer()); 25 } 26 if (isDoubleType(writer)) { 27 writer.assignSerializer(this.defaultDoubleJsonSerializer()); 28 } 29 } 30 return beanProperties; 31 } // 判断是什么类型 32 33 protected boolean isArrayType(BeanPropertyWriter writer) { 34 Class clazz = writer.getPropertyType(); 35 return clazz.isArray() || clazz.equals(List.class) || clazz.equals(Set.class); 36 } 37 38 protected boolean isStringType(BeanPropertyWriter writer) { 39 Class clazz = writer.getPropertyType(); 40 return clazz.equals(String.class); 41 } 42 43 protected boolean isIntegerType(BeanPropertyWriter writer) { 44 Class clazz = writer.getPropertyType(); 45 return clazz.equals(Integer.class) || clazz.equals(int.class) || clazz.equals(Long.class); 46 } 47 48 protected boolean isDoubleType(BeanPropertyWriter writer) { 49 Class clazz = writer.getPropertyType(); 50 return clazz.equals(Double.class) || clazz.equals(BigDecimal.class); 51 } 52 53 54 protected JsonSerializer defaultNullArrayJsonSerializer() { 55 return _nullArrayJsonSerializer; 56 } 57 58 protected JsonSerializer defaultNullStringJsonSerializer() { 59 return _nullStringJsonSerializer; 60 } 61 62 protected JsonSerializer defaultNullIntegerJsonSerializer() { 63 return _nullIntegerJsonSerializer; 64 } 65 66 protected JsonSerializer defaultDoubleJsonSerializer() { 67 return _doubleJsonSerializer; 68 } 69}

应用我们自己bean序列化使其生效 提供MappingJackson2HttpMessageConverter类 在配置类中提供MappingJackson2HttpMessageConverter类,使用ObjectMapper 做全局的序列化

1@Configuration 2public class ClassJsonConfiguration { 3 @Bean 4 public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() { 5 final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); 6 7 ObjectMapper mapper = converter.getObjectMapper(); 8 9 // 为mapper注册一个带有SerializerModifier的Factory,此modifier主要做的事情为:判断序列化类型,根据类型指定为null时的值 10 11 mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier())); 12 13 return converter; 14 } 15}

此类会代替SpringBoot默认的json解析方案。事实上,此类中起作用的是ObjectMapper 类,因此也可直接配置此类。

1 @Bean 2 public ObjectMapper om() { 3 ObjectMapper mapper = new ObjectMapper(); 4 // 为mapper注册一个带有SerializerModifier的Factory,此modifier主要做的事情为:判断序列化类型,根据类型指定为null时的值 5 6 mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier())); 7 return mapper; 8 }

通过上面方式自定义序列化,还可以通过注解 @JsonSerialize序列化自定义如:

1@Component 2public class DoubleSerialize extends JsonSerializer<Double> { 3 4 private DecimalFormat df = new DecimalFormat("##.00"); 5 6 @Override 7 public void serialize(Double value, JsonGenerator gen, SerializerProvider serializers) 8 throws IOException, JsonProcessingException { 9 if(value != null) { 10 gen.writeString(df.format(value)); 11 } 12 } 13}

然后再需要使用字段上面加上

1 @JsonSerialize(using = DoubleJsonSerialize.class) 2 private BigDecimal price; 3

配置文件jackson详细配置

1 spring: 2 jackson: 3 # 设置属性命名策略,对应jackson下PropertyNamingStrategy中的常量值,SNAKE_CASE-返回的json驼峰式转下划线,json body下划线传到后端自动转驼峰式 4 property-naming-strategy: SNAKE_CASE 5 # 全局设置@JsonFormat的格式pattern 6 date-format: yyyy-MM-dd HH:mm:ss 7 # 当地时区 8 locale: zh 9 # 设置全局时区 10 time-zone: GMT+8 11 # 常用,全局设置pojo或被@JsonInclude注解的属性的序列化方式 12 default-property-inclusion: NON_NULL #不为空的属性才会序列化,具体属性可看JsonInclude.Include 13 # 常规默认,枚举类SerializationFeature中的枚举属性为key,值为boolean设置jackson序列化特性,具体key请看SerializationFeature源码 14 serialization: 15 WRITE_DATES_AS_TIMESTAMPS: true # 返回的java.util.date转换成timestamp 16 FAIL_ON_EMPTY_BEANS: true # 对象为空时是否报错,默认true 17 # 枚举类DeserializationFeature中的枚举属性为key,值为boolean设置jackson反序列化特性,具体key请看DeserializationFeature源码 18 deserialization: 19 # 常用,json中含pojo不存在属性时是否失败报错,默认true 20 FAIL_ON_UNKNOWN_PROPERTIES: false 21 # 枚举类MapperFeature中的枚举属性为key,值为boolean设置jackson ObjectMapper特性 22 # ObjectMapper在jackson中负责json的读写、json与pojo的互转、json tree的互转,具体特性请看MapperFeature,常规默认即可 23 mapper: 24 # 使用getter取代setter探测属性,如类中含getName()但不包含name属性与setName(),传输的vo json格式模板中依旧含name属性 25 USE_GETTERS_AS_SETTERS: true #默认false 26 # 枚举类JsonParser.Feature枚举类中的枚举属性为key,值为boolean设置jackson JsonParser特性 27 # JsonParser在jackson中负责json内容的读取,具体特性请看JsonParser.Feature,一般无需设置默认即可 28 parser: 29 ALLOW_SINGLE_QUOTES: true # 是否允许出现单引号,默认false 30 # 枚举类JsonGenerator.Feature枚举类中的枚举属性为key,值为boolean设置jackson JsonGenerator特性,一般无需设置默认即可 31 # JsonGenerator在jackson中负责编写json内容,具体特性请看JsonGenerator.Feature 32 33
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

SpringBoot自定义序列化的使用方式

场景及需求:项目接入了SpringBoot开发,现在需求是服务端接口返回的字段如果为空,那么自动转为空字符串。例如:\    {        "id":1,        "name":null    },    {        "id":2,        "name":"x

Django中Admin中的一些参数配置

设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable