背景
最近定位出一个LocalDateTime序列化相关的问题,简单记录一下。本文重点介绍Jackson对LocalDateTime的序列化和反序列化,并结合Spring应用场景进行介绍。
1.LocalDateTime与字符串转换
可以通过DateTimeFormatter实现LocalDateTime与字符串的相互转换,如下所示:
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static String getFormattedStr(LocalDateTime localDateTime) { return DATE_TIME_FORMATTER.format(localDateTime); } private static LocalDateTime parseLocalDateTime(String formattedStr) { return LocalDateTime.parse(formattedStr, DATE_TIME_FORMATTER); }
测试用例如下:
public class Application { private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) { String formattedStr = getFormattedStr(LocalDateTime.now()); System.out.println(formattedStr); LocalDateTime localDateTime = parseLocalDateTime(formattedStr); System.out.println(localDateTime); } private static String getFormattedStr(LocalDateTime localDateTime) { return DATE_TIME_FORMATTER.format(localDateTime); } private static LocalDateTime parseLocalDateTime(String formattedStr) { return LocalDateTime.parse(formattedStr, DATE_TIME_FORMATTER); } }
测试用例输出结果如下所示:
2024-07-30 21:36:20 2024-07-30T21:36:20
2.Jackson与LocalDatetime
引入pom依赖
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.9.1</version> </dependency>
jackson-datatype-jsr310
在原有Jackson基础上添加了对jsr310的支持, 即提供了序列化和反序列化LocalDateTime/LocalDate/LocalTime的能力,由于使用方式和原理相同,本文以LocalDateTime为例进行介绍。
测试用例:
@Slf4j public class JacksonTest { private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Test @SneakyThrows public void testJackson() { ObjectMapper objectMapper = getObjectMapper(); Account account = new Account(); account.setId(1).setName("name").setAddress("address").setMoney(100).setCtime(LocalDateTime.now()).setSex(Sex.FeMale); String str = objectMapper.writeValueAsString(account); if (LOGGER.isDebugEnabled()) { LOGGER.debug("str is {}.", str); } Account accountCopy = objectMapper.readValue(str,Account.class); if (LOGGER.isDebugEnabled()) { LOGGER.debug("AccountCopy is {}.", accountCopy); } } public ObjectMapper getObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER)); objectMapper.registerModule(javaTimeModule); return objectMapper; } }
得到运行结果:jackson-datatype-jsr310
包中的JavaTimeModule类为ObjectMapper提供了序列化默认配置,这里只需要修改日期的序列化和反序列化时间格式即可。
3.Spring与LocalDatetime
Spring默认使用Jackson实现序列化和反序列的能力
SpringMVC处理客户端的HTTP请求时,会经过以下流程:
在进入业务处理逻辑(Controller层)前,SpringMVC框架会拦截请求🥷通过Convert将application/json格式的数据转化为Java对象;业务处理完成并响应数据时,通过Convert将java对象转换为application/json格式的字符串返回给客户端。Convert处理application/json数据与ava对象的过程中,就涉及序列化和反序列化问题。
首先,看一下案例:在Spring中可以通过注册Jackson2ObjectMapperBuilder这个Bean对象实现序列化配置;
引入pom依赖
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.9.1</version> </dependency>
另外,如果是SpringBoot项目,不需要额外引入该依赖:
@Bean public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); JavaTimeModule javaTimeModule = new JavaTimeModule(); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DATE_TIME_FORMATTER)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DATE_TIME_FORMATTER)); builder.modules(javaTimeModule); return builder; }
上述Bean对象注入到IOC容器后,可以实现LocalDataTime系列(LocalDate)的序列化,这限制了整个SpringMVC使用LocalDateTime的格式,不利于扩展;可通过@JsonFormat注解针对不同的属性定制序列化和反序列化时的日期格式:
@Data @Accessors(chain = true) public class Account { private Integer id; private String name; private Integer money; private String address; private Sex sex; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime ctime; }