Jackson将VersementDTO转为JSON时日期格式化及反序列化问题求助
问题场景
需要将Java对象VersementDTO转换为名为Historique的JSON字符串,该DTO包含LocalDateTime类型的日期字段,存在两个问题:
- Jackson默认将
LocalDateTime序列化为嵌套对象形式,不符合期望的2007/02/21 15:21:24格式 - 反序列化时出现错误,无法将字符串
2007-02-07T15:21:24转换为LocalDateTime类型,报错信息如下:
org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type
java.time.LocalDateTimefrom String "2007-02-07T15:21:24": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2007-02-07T15:21:24' could not be parsed at index 10; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of typejava.time.LocalDateTimefrom String "2007-02-07T15:21:24": Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2007-02-07T15:21:24' could not be parsed at index 10
at [Source: (PushbackInputStream); line: 1, column: 95] (through reference chain: aws.lbackend.dto.VersementDTO["dateValidation"])
当前使用的转换代码
public static String historizeInJson(VersementDTO pojo) { SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm"); df.setTimeZone(TimeZone.getTimeZone("UTC")); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true)); objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false); objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false); try { String jsVDTO = objectMapper.writeValueAsString(pojo); //System.out.print("json dz : "+ jsVDTO); return jsVDTO; } catch (JsonProcessingException e) { LOGGER.info("failed conversion: Pfra object to Json", e); return null; } catch (IOException e) { throw new RuntimeException(e); } }
问题原因
当前代码使用的SimpleDateFormat、StdDateFormat都是针对Java旧日期类型(java.util.Date)的配置,无法作用于Java 8+的新时间API类型(LocalDateTime),需要使用Jackson专门的jackson-datatype-jsr310模块来支持这类类型的序列化和反序列化。
解决步骤
- 引入依赖:确保项目中引入了
jackson-datatype-jsr310模块(Maven示例):
<dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>你的Jackson版本号</version> </dependency>
- 修改ObjectMapper配置:注册
JavaTimeModule,并自定义LocalDateTime的序列化/反序列化格式,同时兼容反序列化时的ISO格式(即带T的字符串):
修改后的完整代码
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.TimeZone; public static String historizeInJson(VersementDTO pojo) { // 定义期望的日期输出格式 DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); // 定义兼容反序列化的多格式规则:同时支持目标格式和带T的ISO格式 DateTimeFormatter deserializeFormatter = DateTimeFormatter.ofPattern("[yyyy/MM/dd HH:mm:ss][yyyy-MM-dd'T'HH:mm:ss]"); ObjectMapper objectMapper = new ObjectMapper(); // 注册JavaTimeModule以支持Java 8+时间类型 JavaTimeModule timeModule = new JavaTimeModule(); // 配置LocalDateTime序列化器:输出指定格式的字符串 timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(outputFormatter)); // 配置LocalDateTime反序列化器:兼容两种输入格式 timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(deserializeFormatter)); objectMapper.registerModule(timeModule); // 统一设置时区为UTC objectMapper.setTimeZone(TimeZone.getTimeZone("UTC")); // 关闭时间戳输出,确保日期以字符串形式返回 objectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); objectMapper.configure(com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS, false); try { return objectMapper.writeValueAsString(pojo); } catch (JsonProcessingException e) { LOGGER.info("failed conversion: Pfra object to Json", e); return null; } catch (IOException e) { throw new RuntimeException(e); } }
代码说明
JavaTimeModule:Jackson专门为Java 8+时间API提供的模块,必须注册才能处理LocalDateTime等新时间类型LocalDateTimeSerializer:自定义序列化逻辑,将LocalDateTime转为指定的yyyy/MM/dd HH:mm:ss格式字符串LocalDateTimeDeserializer:使用带方括号的格式模式,同时兼容目标格式和带T的ISO格式输入,直接解决反序列化报错问题- 统一设置UTC时区:确保日期处理的时区一致性,避免跨时区导致的日期偏差
内容的提问来源于stack exchange,提问作者Organon

