前言
Jackson 是 Spring boot(Spring web) 自带的序列化、反序列化工具
本文将通过 Jackson 解决如下问题
- 后端返回 Long 类型,前端精度丢失
- 全局配置时间类型的序列化与反序列化格式
配置文件
yaml
yaml 只能配置部分功能,无法实现
- 后端返回 Long 类型,前端精度丢失
- 全局配置
java.time
包下时间类型的序列化与反序列化格式
spring:
jackson:
# 设置 java.util.Date, Calendar 序列化、反序列化的格式
date-format: yyyy-MM-dd HH:mm:ss
# 当地时区
locale: zh
# 设置全局时区
time-zone: GMT+8
# 设置对象或被@JsonInclude注解的属性的序列化方式。NON_NULL 表示不为空的属性才会序列化,具体属性可看JsonInclude.Include
# default-property-inclusion: NON_NULL
serialization:
# 禁止将 java.util.Date, Calendar 序列化为数字(时间戳)
WRITE_DATES_AS_TIMESTAMPS: false
# 序列化时,对象为 null,是否抛异常
FAIL_ON_EMPTY_BEANS: false
deserialization:
# 反序列化时,json 中包含 pojo 不存在属性时,是否抛异常
FAIL_ON_UNKNOWN_PROPERTIES: false
配置类
可以实现上述提到的全部功能
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
@Configuration
public class JacksonConfig {
private static final String DEFAULT_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
// 这一部分也可以在 yaml 中配置
builder
// 序列化时,对象为 null,是否抛异常
.failOnEmptyBeans(false)
// 反序列化时,json 中包含 pojo 不存在属性时,是否抛异常
.failOnUnknownProperties(false)
// 禁止将 java.util.Date, Calendar 序列化为数字(时间戳)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
// 设置 java.util.Date, Calendar 序列化、反序列化的格式
.dateFormat(new SimpleDateFormat(DEFAULT_DATETIME_PATTERN))
// 设置 java.util.Date、Calendar 序列化、反序列化的时区
.timeZone(TimeZone.getTimeZone("GMT+8"))
;
// Jackson 序列化 long类型为String,解决后端返回的Long类型在前端精度丢失的问题
builder.serializerByType(BigInteger.class, ToStringSerializer.instance);
builder.serializerByType(Long.class, ToStringSerializer.instance);
builder.serializerByType(Long.TYPE, ToStringSerializer.instance);
// 配置 Jackson 反序列化 LocalDateTime、LocalDate、LocalTime 时使用的格式
builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)));
builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
builder.deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
// 配置 Jackson 序列化 LocalDateTime、LocalDate、LocalTime 时使用的格式
builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)));
builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
builder.serializers(new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
};
}
}
后记
在 Spring boot 中,只有是 Post+json 提交的时间格式字符串,才会使用Jackson 进行反序列化,如果是 Get、Post+表单的提交方式,由于不是 json 格式,无法使用 Jackson 进行反序列化。一般会搭配 Spring 提供的 @DateTimeFormat
注解进行局部处理。
但也有全局处理的解决方案,具体参考 Spring boot 中时间类型的序列化与反序列化
补充
在 Spring boot 中使用 Jackson ,更建议的做法是创建一个 Jackson 工具类,把 Jackson2ObjectMapperBuilderCustomizer
封装到工具类里,并通过 Jackson2ObjectMapperBuilderCustomizer
构建 ObjectMapper
后续需要用到 Jackson2ObjectMapperBuilderCustomizer
或 ObjectMapper
的地方,如 Spring Web 对传参的反序列化和返回值的序列化、redis 存储对象的序列化和获取对象的反序列化以及业务代码中使用ObjectMapper
,都从Jackson 工具类获取,从而达成一致的使用体验。
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
/**
* Jackson 基础工具类,提供了一个配置好的 ObjectMapper 供全局使用,以实现全局统一的序列化、反序列化规范
*/
public class BaseJacksonUtil {
private static final String DEFAULT_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
private static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
public static final Jackson2ObjectMapperBuilderCustomizer CUSTOMIZER = jackson2ObjectMapperBuilderCustomizer();
public static final ObjectMapper MAPPER = getObjectMapper();
/**
* 构建 Jackson 自定义配置
*
* @return
*/
public static Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
builder
// 序列化时,对象为 null,是否抛异常
.failOnEmptyBeans(false)
// 反序列化时,json 中包含 pojo 不存在属性时,是否抛异常
.failOnUnknownProperties(false)
// 禁止将 java.util.Date、Calendar 序列化为数字(时间戳)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
// 设置 java.util.Date, Calendar 序列化、反序列化的格式
.dateFormat(new SimpleDateFormat(DEFAULT_DATETIME_PATTERN))
// 设置 java.util.Date, Calendar 序列化、反序列化的时区
.timeZone(TimeZone.getTimeZone("GMT+8"));
// Jackson 序列化 long类型为String,解决后端返回的Long类型在前端精度丢失的问题
builder.serializerByType(BigInteger.class, ToStringSerializer.instance);
builder.serializerByType(Long.class, ToStringSerializer.instance);
builder.serializerByType(Long.TYPE, ToStringSerializer.instance);
// 配置 Jackson 反序列化 LocalDateTime、LocalDate、LocalTime 时使用的格式
builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)));
builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
builder.deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
// 配置 Jackson 序列化 LocalDateTime、LocalDate、LocalTime 时使用的格式
builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATETIME_PATTERN)));
builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
builder.serializers(new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
};
}
/**
* 根据 Jackson2ObjectMapperBuilderCustomizer 构建 ObjectMapper
*
* @return ObjectMapper
*/
public static ObjectMapper getObjectMapper() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
jackson2ObjectMapperBuilderCustomizer().customize(builder);
return builder.build();
}
}
Spring web 配置 Jackson2ObjectMapperBuilderCustomizer
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return BaseJacksonUtil.customizer;
}
}
自定义 RedisTemplate 配置 ObjectMapper
@Configuration
public class RedisConfig {
@Bean
RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 配置 json 序列化器 - Jackson2JsonRedisSerializer
Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
jacksonSerializer.setObjectMapper(BaseJacksonUtil.MAPPER);
// 创建并配置自定义 RedisTemplateRedisOperator
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 将 key 序列化成字符串
template.setKeySerializer(new StringRedisSerializer());
// 将 hash 的 key 序列化成字符串
template.setHashKeySerializer(new StringRedisSerializer());
// 将 value 序列化成 json
template.setValueSerializer(jacksonSerializer);
// 将 hash 的 value 序列化成 json
template.setHashValueSerializer(jacksonSerializer);
// 设置连接器
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}