Spring Boot 3中ObjectMapper反序列化List<Map>到DTO时Instant字段报错
解决Spring Boot 3升级后Instant反序列化异常问题
问题原因
Spring Boot 3升级后,底层JdbcTemplate对数据库datetimeoffset类型的处理逻辑发生变化:
- Spring Boot 2中,该类型会被转为字符串返回
- Spring Boot 3中,默认转为OffsetDateTime对象返回
当你用ObjectMapper将List<Map<String,Object>>转换为DTO时,原本期望字符串转Instant,但实际是OffsetDateTime对象转Instant,Jackson默认没有对应的转换器,因此抛出Cannot deserialize value of type java.time.Instant from Object value异常。
解决方案
方案1:修改DTO字段类型(推荐,贴合数据模型)
数据库datetimeoffset带时区信息,Java中OffsetDateTime是最匹配的类型,直接替换DTO中的Instant:
public class DTO { // 其他字段保持不变 private OffsetDateTime createdDate; private OffsetDateTime lastModifiedDate; // 如需对外提供Instant类型,可添加转换方法 public Instant getCreatedDateAsInstant() { return createdDate != null ? createdDate.toInstant() : null; } public Instant getLastModifiedDateAsInstant() { return lastModifiedDate != null ? lastModifiedDate.toInstant() : null; } // getter、setter、构造方法不变 }
此方案无需额外配置,ObjectMapper可直接完成赋值。
方案2:配置ObjectMapper支持类型转换
若不想修改DTO,可自定义ObjectMapper,添加OffsetDateTime到Instant的转换器:
@Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); JavaTimeModule timeModule = new JavaTimeModule(); // 注册OffsetDateTime转Instant的转换器 timeModule.addConverter(new Converter<OffsetDateTime, Instant>() { @Override public Instant convert(OffsetDateTime source) { return source != null ? source.toInstant() : null; } }); objectMapper.registerModule(timeModule); // 禁用日期序列化时间戳,统一用字符串格式 objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return objectMapper; } }
方案3:修改SQL查询,返回字符串格式日期
在查询SQL中,将datetimeoffset字段转换为ISO标准字符串,确保Map中对应值为字符串类型:
以SQL Server为例:
SELECT loanId, obligor, -- 转换为ISO 8601格式字符串 CONVERT(varchar(50), lastModifiedDate, 127) AS lastModifiedDate, CONVERT(varchar(50), createdDate, 127) AS createdDate, -- 其他字段... FROM your_target_table
转换后的字符串格式为2024-05-09T15:46:30.199154Z,Jackson可直接反序列化为Instant。
内容的提问来源于stack exchange,提问作者aakash singh
相关产品推荐
相关产品推荐

