前言
在我们开发项目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自动配置了Jackson和Gson的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
- 使用
@JsonFormat注解自定义格式
1 @JsonFormat(pattern = "yyyy-MM-dd") 2 private Date birthday;
但是这种要对每个实体类中的日期字段都需要添加此注解不够灵活
- 全局添加
在配置文件中直接添加
spring.jackson.date-format=yyyy-MM-dd
NULL字段不返回
- 在接口中如果不需要返回null字段可以使用
@JsonInclude注解
1 @JsonInclude(JsonInclude.Include.NON_NULL) 2 private String title;
但是这种要对每个实体类中的字段都需要添加此注解不够灵活
- 全局添加 在配置文件中直接添加
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
