创建一个测试类:
@Data
public class TestResponse {
@ApiModelProperty("未格式化时间")
private LocalDateTime localDateTime;
@ApiModelProperty("未格式化时间")
private LocalDate localDate;
@ApiModelProperty("未格式化时间")
private Date date;
}
restApi:
@GetMapping("test")
@ApiOperation("响应测试")
public TestResponse test(){
TestResponse response = new TestResponse();
response.setDate(new Date());
response.setLocalDateTime(LocalDateTime.now());
response.setLocalDate(LocalDate.now());
return response;
}
响应参数带Time的采用的是“ yyyy-MM-dd’T’HH:mm:ss.SSS ”格式,并且DATE默认使用的是UTC时间,比北京时间慢。
在字段上加上注解@JsonFormat
@ApiModelProperty("已格式化时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;
@ApiModelProperty("已格式化时间")
@JsonFormat(pattern = "yyyy年MM月dd日")
private LocalDate localDate;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") //date默认使用UTC时间,北京时间得+8
@ApiModelProperty("已格式化时间")
private Date date;
@JsonFormat是jakson自带注解(spring家族默认使用jakson进行序列化与反序列化),常用属性如下:
属性 | 作用 |
---|---|
pattern | 格式化模板,如时间的’yyyy-MM-dd HH:mm:ss |
shape | 决定数据类型,如NUMBER、ARRAY,默认为ANY,任何类型 |
locale | 语言环境 |
timezone | 时区,默认为UTC |
在JSR310FormattedSerializerBase类createContextual方法中会检查序列化器,首先会获取目标对象字段上的JsonFormat,如果JsonFormat 不为空,会读取其属性值。根据属性值创建DateTimeFormatter对象,并将其设置为序列化器模板。
最终通过HttpMessageConverter接口实现类的子类MappingJackson2HttpMessageConverter输出响应。MappingJackson2HttpMessageConverter为默认的Json转换类。
也就是更改了LocalDateSerializer、LocalDateTimeSerializer:
spring:
jackson:
# 全局配置,但是对LocalDateTime和LocalDate无效
date-format: yyyy-MM-dd HH:mm:ss
# 时区
time-zone: GMT+8
重启,可以看到我们在配置文件中设置的格式与时区注入进了ObjectMapper.
因为未对LocalDateSerializer和LocalDateTimeSerializer进行改动,结果只对date生效。
手写ObjectMapper配置类:
@Configuration
public class DateTimeConfiguration {
@Bean
public ObjectMapper initObjectMapper(){
ObjectMapper objectMapper=new ObjectMapper();
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//JavaTimeModule 是jackson通过jsr310引入的JAVA8格式化类,没有此Module,序列化后的值会比较乱
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
}
运行结果同之前YML中配置一样:
所以LocalDateSerializer和LocalDateTimeSerializer中也得按我们需要的格式进行序列化:
@Bean
public ObjectMapper initObjectMapper(){ //自定义ObjectMapper配置并注册BEAN
ObjectMapper objectMapper=new ObjectMapper();
JavaTimeModule javaTimeModule=new JavaTimeModule();
//针对LocalDateTime和LocalDate
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")));
//针对Date类
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//JavaTimeModule 是jackson通过jsr310引入的JAVA8格式化类,没有此Module,序列化后的值会比较乱
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
上面localdate字段未被全局序列化覆盖,因为字段上加了注解,说明局部优先于全局:
去掉注解:
也可以通过Jackson2ObjectMapperBuilder来构建新的ObjectMapper实例设置序列化与反序列化方式:
@Configuration
public class DateTimeConfiguration {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { //通过ObjectMapper构造器构建实例
return builder -> {
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //date格式,序列化和反序列化都是它
//序列化
builder.serializerByType(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
builder.serializerByType(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
//反序列化
builder.deserializerByType(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
builder.deserializerByType(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
};
}
// @Bean
// public ObjectMapper initObjectMapper(){ //自定义ObjectMapper配置并注册BEAN
// ObjectMapper objectMapper=new ObjectMapper();
// JavaTimeModule javaTimeModule=new JavaTimeModule();
// //针对LocalDateTime和LocalDate
// javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// //针对Date类
// objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// //JavaTimeModule 是jackson通过jsr310引入的JAVA8格式化类,没有此Module,序列化后的值会比较乱
// objectMapper.registerModule(javaTimeModule);
// return objectMapper;
// }
}
创建测试接口
@PostMapping("body")
@ApiOperation("json请求测试")
public TestResponse body(@RequestBody TestResponse testResponse){
log.info("打印的body:{}",testResponse);
return testResponse;
}
因为LocalDateTimeDeserializer与LocalDateDeserializer默认格式模板分别为’yyyy-MM-ddTHH:mm:ss 、'yyyy-MM-dd '.
我们按照默认格式传输自然没问题:
同样,我们也可以像局部序列化一样局部反序列化时间。
还是使用@JsonFormat注解(序列化与反序列化一致),此时,标了注解字段全局序列化配置失效:
@ApiModelProperty("已格式化时间")
@JsonFormat(pattern = "yyyy年MM月dd日HH时mm分ss秒")
private LocalDateTime localDateTime;
@ApiModelProperty("已格式化时间")
@JsonFormat(pattern = "yyyy年MM月dd日")
private LocalDate localDate;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS") //date默认使用UTC时间,北京时间得+8
@ApiModelProperty("已格式化时间")
private Date date;
同序列化时相同,JSR310DateTimeDeserializerBase反序列化时也是读取注解中的属性值,创建新的JsonDeserializer(包括LocalDateTimeDeserializer、LocalDateDeserializer等)
序列化我们用了全局配置,反序列化自然也应该使用全局配置
去掉注解:
再已有全局配置加上反序列化方式:
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //date格式,序列化和反序列化都是它
//序列化
builder.serializerByType(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
builder.serializerByType(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
//反序列化
builder.deserializerByType(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
builder.deserializerByType(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
};
}
@Bean
public ObjectMapper initObjectMapper(){ //自定义ObjectMapper配置并注册BEAN
ObjectMapper objectMapper=new ObjectMapper();
JavaTimeModule javaTimeModule=new JavaTimeModule();
//针对LocalDateTime和LocalDate
//序列化
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.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
//针对Date类 序列化和反序列化都是它
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//JavaTimeModule 是jackson通过jsr310引入的JAVA8格式化类,没有此Module,序列化后的值会比较乱
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
// @Bean
// public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
// return builder -> {
// builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //date格式,序列化和反序列化都是它
// //序列化
// builder.serializerByType(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
// builder.serializerByType(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
// //反序列化
// builder.deserializerByType(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
// builder.deserializerByType(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
// };
// }
将接口改入参改成非JSON类型
@PostMapping("param")
@ApiOperation("普通参数请求测试")
public TestResponse body(TestResponse testResponse){
try {
log.info("打印的param:{}",testResponse);
} catch (Exception e) {
e.printStackTrace();
}
return testResponse;
}
我们再字段上加上注解@DateTimeFormat
@ApiModelProperty("已格式化时间")
@DateTimeFormat(pattern = "yyyy年MM月dd日HH时mm分ss秒")
private LocalDateTime localDateTime;
@ApiModelProperty("已格式化时间")
@DateTimeFormat(pattern = "yyyy年MM月dd日")
private LocalDate localDate;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
@ApiModelProperty("已格式化时间")
private Date date;
按对应格式输入参数测试:
非json参数由GenericConversionService类转换,如果由标记了@DateTimeFormat注解,Jsr310DateTimeFormatAnnotationFormatterFactory会返回新一个解析类对象:
FormattingConversionService做为实现类会通过Parser类对象生成GenericConverter并进行转换:
/**
* string to date
* @return date
*/
@Bean
public DateConverter dateConverter () {
return new DateConverter();
}
/**
* string to localDate
* @return localDate
*/
@Bean
public LocalDateConverter localDateConverter () {
return new LocalDateConverter();
}
/**
* string to LocalDateTime
* @return LocalDateTime
*/
@Bean
public LocalDateTimeConverter localDateTimeConverter () {
return new LocalDateTimeConverter();
}
static class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
String pattern ;
if (source.length() <= "yyyy-MM-dd".length()) {
pattern = "yyyy-MM-dd";
}else {
pattern = "yyyy-MM-dd HH:mm:ss.SSS";
}
SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
try {
return dateFormat.parse(source);
} catch (ParseException e) {
throw new RuntimeException(source + " to date error!");
}
}
}
static class LocalDateConverter implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
}
}
static class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒"));
}
}
配置类最终代码:
package com.example.demo.config;
import cn.hutool.core.date.DatePattern;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.std.NullSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.DurationSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.core.convert.converter.Converter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone;
@Configuration
public class DateTimeConfiguration {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //date格式,序列化和反序列化都是它
//序列化
builder.serializerByType(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
builder.serializerByType(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
//反序列化
builder.deserializerByType(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
builder.deserializerByType(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
};
}
// @Bean
// public ObjectMapper initObjectMapper(){ //自定义ObjectMapper配置并注册BEAN
// ObjectMapper objectMapper=new ObjectMapper();
// JavaTimeModule javaTimeModule=new JavaTimeModule();
// //针对LocalDateTime和LocalDate
// //序列化
// 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.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
// javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// //针对Date类 序列化和反序列化都是它
// objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// //JavaTimeModule 是jackson通过jsr310引入的JAVA8格式化类,没有此Module,序列化后的值会比较乱
// objectMapper.registerModule(javaTimeModule);
// return objectMapper;
// }
//以下针对非JSON参数
/**
* string to date
* @return date
*/
@Bean
public DateConverter dateConverter () {
return new DateConverter();
}
/**
* string to localDate
* @return localDate
*/
@Bean
public LocalDateConverter localDateConverter () {
return new LocalDateConverter();
}
/**
* string to LocalDateTime
* @return LocalDateTime
*/
@Bean
public LocalDateTimeConverter localDateTimeConverter () {
return new LocalDateTimeConverter();
}
static class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
String pattern ;
if (source.length() <= "yyyy-MM-dd".length()) {
pattern = "yyyy-MM-dd";
}else {
pattern = "yyyy-MM-dd HH:mm:ss.SSS";
}
SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
try {
return dateFormat.parse(source);
} catch (ParseException e) {
throw new RuntimeException(source + " to date error!");
}
}
}
static class LocalDateConverter implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
}
}
static class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒"));
}
}
}
我们返回级前端的各种类型的数据都可能为空,这时候一般我们返回的是NULL,有时候前端会觉得这样不够友好。
如果要转换的话,一般是将字符串转为空串"",集合转为空数组[],数字类转为0,布尔型转为false。本文我们就照着此目标来实现。当然,和前端约定好最重要,怎么方便怎么来,标准毕竟是人定的。
实体类添加些字段:
@Data
public class TestResponse {
@ApiModelProperty("字符串为NULL")
private String str;
@ApiModelProperty("已格式化时间")
// @DateTimeFormat(pattern = "yyyy年MM月dd日HH时mm分ss秒")
private LocalDateTime localDateTime;
@ApiModelProperty("已格式化时间")
// @DateTimeFormat(pattern = "yyyy年MM月dd日")
private LocalDate localDate;
// @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
@ApiModelProperty("已格式化时间")
private Date date;
@ApiModelProperty("集合为NULL")
private List<String> list;
@ApiModelProperty("布尔型为NULL")
private Boolean bool;
@ApiModelProperty("数字型为NULL")
private Integer anInt;
@ApiModelProperty("LocalDateTime为NULL")
private LocalDateTime nullLocalDateTime;
@ApiModelProperty("LocalDate为NULL")
private LocalDate nullLocalDate;
@ApiModelProperty("Date为NULL")
private Date nullDate;
}
依然用之前的测试接口,依然只对几个旧有字段赋值。
@GetMapping("test")
@ApiOperation("响应测试")
public TestResponse test(){
TestResponse response = new TestResponse();
response.setDate(new Date());
response.setLocalDateTime(LocalDateTime.now());
response.setLocalDate(LocalDate.now());
return response;
}
可以加注解或者编写配置类将为null的字段忽略掉,但是这不在本文讨论范畴。
我们可以实现WebMvcConfigurer并重写extendMessageConverters,更改HttpMessageConverter列表,已达到自定义消息转换的目的:
@Configuration
@Slf4j
public class MvcConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//fastJson写法
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.WriteNullStringAsEmpty, // 将String类型的字段如果为null,输出为"",而非null
SerializerFeature.WriteNullNumberAsZero, // 将Number类型的字段如果为null,输出为0,而非null
SerializerFeature.WriteNullListAsEmpty, // 将List类型的字段如果为null,输出为[],而非null
SerializerFeature.WriteNullBooleanAsFalse, // 将Boolean类型的字段如果为null,输出为false,而非null
SerializerFeature.DisableCircularReferenceDetect // 避免循环引用
);
fastConverter.setFastJsonConfig(fastJsonConfig);
converters.add(0, fastConverter);
}
}
测试结果:
可以看到除了时间类以外,所有Null字段都转换成了我们自定义的格式。
converters表示HttpMessageConverter列表,其中的元素MappingJackson2HttpMessageConverter做为AbstractJackson2HttpMessageConverter子类是spring中默认的json转换器。我们新建的FastJsonHttpMessageConverter也是AbstractJackson2HttpMessageConverter子类,并且排在默认的MappingJackson2HttpMessageConverter之前,所以替代了MappingJackson2HttpMessageConverter,如果是在MappingJackson2HttpMessageConverter之后是没用的:
重启后:
我们将代码恢复,查看生效时的结果,发现时间类并不是我们定义的格式:
因为我们使用的fastjson,而不是系统默认的Jackson。虽然fastjson效率高但是用在这里没Jackson合适。
这里采用网上看到的写法,自定义MappingJackson2HttpMessageConverter继承类消息转换器,并在内部继承BeanSerializerModifier,重写changeProperties方法(重点):
自定义消息转换器
class JacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {
/**
* 处理数组类型的null值
*/
public class NullArrayJsonSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
if (value == null) {
jgen.writeStartArray();
jgen.writeEndArray();
}
}
}
/**
* 处理字符串类型的null值
*/
public class NullStringJsonSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(StringUtils.EMPTY);
}
}
/**
* 处理数字类型的null值
*/
public class NullNumberJsonSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeNumber(0);
}
}
/**
* 处理布尔类型的null值
*/
public class NullBooleanJsonSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeBoolean(false);
}
}
public class MyBeanSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
//循环所有的beanPropertyWriter
for (Object beanProperty : beanProperties) {
BeanPropertyWriter writer = (BeanPropertyWriter) beanProperty;
//判断字段的类型,如果是array,list,set则注册nullSerializer
if (isArrayType(writer)) {
//给writer注册一个自己的nullSerializer
writer.assignNullSerializer(new NullArrayJsonSerializer());
} else if (isNumberType(writer)) {
writer.assignNullSerializer(new NullNumberJsonSerializer());
} else if (isBooleanType(writer)) {
writer.assignNullSerializer(new NullBooleanJsonSerializer());
} else if (isStringType(writer)) {
writer.assignNullSerializer(new NullStringJsonSerializer());
} else if (isDateType(writer)){
writer.assignNullSerializer(new NullStringJsonSerializer());
}
else if (isLcDateTimeType(writer)) {
writer.assignNullSerializer(new NullStringJsonSerializer());
}else if (isLcDateType(writer)) {
writer.assignNullSerializer(new NullStringJsonSerializer());
}
}
return beanProperties;
}
/**
* 是否是数组
*/
private boolean isArrayType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
}
/**
* 是否是string
*/
private boolean isStringType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
}
/**
* 是否是int
*/
private boolean isNumberType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return Number.class.isAssignableFrom(clazz);
}
/**
* 是否是boolean
*/
private boolean isBooleanType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.equals(Boolean.class);
}
/**
* 是否是LocalDate
*/
private boolean isDateType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.equals(Date.class);
}
/**
* 是否是LocalDate
*/
private boolean isLcDateType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.equals(LocalDate.class);
}
/**
* 是否是LocalDateTime
*/
private boolean isLcDateTimeType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.equals(LocalDateTime.class);
}
}
//自定义转换会让全局LocalDateTime序列化失效,默认转Long型
JacksonHttpMessageConverter() {
getObjectMapper()
.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))//只对Date生效
.setSerializerFactory(getObjectMapper().getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
}
}
将自定义转换器添加到转换器列表首位:
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0,new JacksonHttpMessageConverter());
}
在自定义转换器中添加如下代码:
/**
* 处理LocalDateTime类型的值
*/
public class LocalDateTimeSerializer extends JsonSerializer<Object> {
private final DateTimeFormatter formatter= DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(formatter.format((LocalDateTime) o));
}
}
/**
* 处理LocalDateTime类型的值
*/
public class LocalDateSerializer extends JsonSerializer<Object> {
private final DateTimeFormatter formatter= DateTimeFormatter.ofPattern("yyyy年MM月dd日");
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(formatter.format((LocalDate) o));
}
}
修改内部类MyBeanSerializerModifier的changeProperties方法,主要是添加上面定义的序列化方法:
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
//循环所有的beanPropertyWriter
for (Object beanProperty : beanProperties) {
BeanPropertyWriter writer = (BeanPropertyWriter) beanProperty;
//判断字段的类型,如果是array,list,set则注册nullSerializer
if (isArrayType(writer)) {
//给writer注册一个自己的nullSerializer
writer.assignNullSerializer(new NullArrayJsonSerializer());
} else if (isNumberType(writer)) {
writer.assignNullSerializer(new NullNumberJsonSerializer());
} else if (isBooleanType(writer)) {
writer.assignNullSerializer(new NullBooleanJsonSerializer());
} else if (isStringType(writer)) {
writer.assignNullSerializer(new NullStringJsonSerializer());
} else if (isDateType(writer)){
writer.assignNullSerializer(new NullStringJsonSerializer());
}
else if (isLcDateTimeType(writer)) {
//非空使用自定义序列化方式
writer.assignSerializer(new LocalDateTimeSerializer());
writer.assignNullSerializer(new NullStringJsonSerializer());
}else if (isLcDateType(writer)) {
//非空使用自定义序列化方式
writer.assignSerializer(new LocalDateSerializer());
writer.assignNullSerializer(new NullStringJsonSerializer());
}
}
return beanProperties;
}
结果符合:
大功告成了是吧?没那么简单,自定义转换器后时间序列化与反序列化配置都会失效(使用默认序列化格式),而我们刚才只配置了序列,并没有配置反序列化。
看下我们之前配置的反序列化方式(JSON类型):
调整参数测试接口为json类型:
转换失败:
说明json入参也得重新序列化
我们之前通过重写BeanSerializerModifier的方法改变序列化格式,现在也通过重写BeanDeserializerModifier的方法改变反序列化格式。
修改代码:
//反序列化修改器
public class MyBeanDeSerializerModifier extends BeanDeserializerModifier {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
Class<?> aClass = deserializer.handledType();
if (aClass.equals(LocalDateTime.class)) {
deserializer=new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
}
if (aClass.equals(LocalDate.class)) {
deserializer=new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
}
return deserializer;
}
}
//自定义转换会让全局LocalDateTime序列化失效,默认转Long型
JacksonHttpMessageConverter() {
DeserializerFactory dFactory = BeanDeserializerFactory.instance.withDeserializerModifier(new MyBeanDeSerializerModifier());
ObjectMapper objectMapper = new ObjectMapper(null, null, new DefaultDeserializationContext.Impl(dFactory));
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))//只对Date生效
.setSerializerFactory(getObjectMapper().getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
setObjectMapper(objectMapper);
}
最终代码如下:
时间入参格式化配置
@Configuration
public class DateTimeConfiguration {
// @Bean
// public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { //时间序列化在
// return builder -> {
// builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //date格式,序列化和反序列化都是它
// //序列化
// builder.serializerByType(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
// builder.serializerByType(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
// //反序列化
// builder.deserializerByType(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒")));
// builder.deserializerByType(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
// };
// }
// @Bean
// public ObjectMapper initObjectMapper(){ //自定义ObjectMapper配置并注册BEAN
// ObjectMapper objectMapper=new ObjectMapper();
// JavaTimeModule javaTimeModule=new JavaTimeModule();
// //针对LocalDateTime和LocalDate
// //序列化
// 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.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
// javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// //针对Date类 序列化和反序列化都是它
// objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// //JavaTimeModule 是jackson通过jsr310引入的JAVA8格式化类,没有此Module,序列化后的值会比较乱
// objectMapper.registerModule(javaTimeModule);
// return objectMapper;
// }
//以下针对非JSON参数
/**
* string to date
* @return date
*/
@Bean
public DateConverter dateConverter () {
return new DateConverter();
}
/**
* string to localDate
* @return localDate
*/
@Bean
public LocalDateConverter localDateConverter () {
return new LocalDateConverter();
}
/**
* string to LocalDateTime
* @return LocalDateTime
*/
@Bean
public LocalDateTimeConverter localDateTimeConverter () {
return new LocalDateTimeConverter();
}
static class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
String pattern ;
if (source.length() <= "yyyy-MM-dd".length()) {
pattern = "yyyy-MM-dd";
}else {
pattern = "yyyy-MM-dd HH:mm:ss.SSS";
}
SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
try {
return dateFormat.parse(source);
} catch (ParseException e) {
throw new RuntimeException(source + " to date error!");
}
}
}
static class LocalDateConverter implements Converter<String, LocalDate> {
@Override
public LocalDate convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
}
}
static class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
}
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒"));
}
}
自定义消息转换
class JacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {
/**
* 处理数组类型的null值
*/
public class NullArrayJsonSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
if (value == null) {
jgen.writeStartArray();
jgen.writeEndArray();
}
}
}
/**
* 处理字符串类型的null值
*/
public class NullStringJsonSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(StringUtils.EMPTY);
}
}
/**
* 处理数字类型的null值
*/
public class NullNumberJsonSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeNumber(0);
}
}
/**
* 处理布尔类型的null值
*/
public class NullBooleanJsonSerializer extends JsonSerializer<Object> {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeBoolean(false);
}
}
/**
* 处理LocalDateTime类型的值
*/
public class LocalDateTimeSerializer extends JsonSerializer<Object> {
private final DateTimeFormatter formatter= DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(formatter.format((LocalDateTime) o));
}
}
/**
* 处理LocalDateTime类型的值
*/
public class LocalDateSerializer extends JsonSerializer<Object> {
private final DateTimeFormatter formatter= DateTimeFormatter.ofPattern("yyyy年MM月dd日");
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(formatter.format((LocalDate) o));
}
}
//序列化修改器
public class MyBeanSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
//循环所有的beanPropertyWriter
for (Object beanProperty : beanProperties) {
BeanPropertyWriter writer = (BeanPropertyWriter) beanProperty;
//判断字段的类型,如果是array,list,set则注册nullSerializer
if (isArrayType(writer)) {
//给writer注册一个自己的nullSerializer
writer.assignNullSerializer(new NullArrayJsonSerializer());
} else if (isNumberType(writer)) {
writer.assignNullSerializer(new NullNumberJsonSerializer());
} else if (isBooleanType(writer)) {
writer.assignNullSerializer(new NullBooleanJsonSerializer());
} else if (isStringType(writer)) {
writer.assignNullSerializer(new NullStringJsonSerializer());
} else if (isDateType(writer)){
writer.assignNullSerializer(new NullStringJsonSerializer());
}
else if (isLcDateTimeType(writer)) {
//非空使用自定义序列化方式
writer.assignSerializer(new LocalDateTimeSerializer());
writer.assignNullSerializer(new NullStringJsonSerializer());
}else if (isLcDateType(writer)) {
//非空使用自定义序列化方式
writer.assignSerializer(new LocalDateSerializer());
writer.assignNullSerializer(new NullStringJsonSerializer());
}
}
return beanProperties;
}
/**
* 是否是数组
*/
private boolean isArrayType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
}
/**
* 是否是string
*/
private boolean isStringType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
}
/**
* 是否是int
*/
private boolean isNumberType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return Number.class.isAssignableFrom(clazz);
}
/**
* 是否是boolean
*/
private boolean isBooleanType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.equals(Boolean.class);
}
/**
* 是否是LocalDate
*/
private boolean isDateType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.equals(Date.class);
}
/**
* 是否是LocalDate
*/
private boolean isLcDateType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.equals(LocalDate.class);
}
/**
* 是否是LocalDateTime
*/
private boolean isLcDateTimeType(BeanPropertyWriter writer) {
Class<?> clazz = writer.getType().getRawClass();
return clazz.equals(LocalDateTime.class);
}
}
//反序列化修改器
public class MyBeanDeSerializerModifier extends BeanDeserializerModifier {
@Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
Class<?> aClass = deserializer.handledType();
if (aClass.equals(LocalDateTime.class)) {
deserializer=new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
}
if (aClass.equals(LocalDate.class)) {
deserializer=new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
}
return deserializer;
}
}
//自定义转换会让全局LocalDateTime序列化失效,默认转Long型
JacksonHttpMessageConverter() {
DeserializerFactory dFactory = BeanDeserializerFactory.instance.withDeserializerModifier(new MyBeanDeSerializerModifier());
ObjectMapper objectMapper = new ObjectMapper(null, null, new DefaultDeserializationContext.Impl(dFactory));
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))//只对Date生效
.setSerializerFactory(getObjectMapper().getSerializerFactory().withSerializerModifier(new MyBeanSerializerModifier()));
setObjectMapper(objectMapper);
配置消息转换器
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(0,new JacksonHttpMessageConverter());
}
}