Quarkus中自定义OffsetDateTime反序列化失效,如何正确配置?
解决Quarkus中OffsetDateTime自定义反序列化器不生效问题
问题场景
前端通过datetime-local控件发送格式为2024-06-01T05:55:04的日期,后端Quarkus的REST控制器接收时无法将该格式解析为OffsetDateTime,返回HTTP 400错误。调试发现默认的InstantDeserializer被调用,自定义反序列化器未生效。
现有代码结构
REST控制器与实体类
@Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class ExampleResource { @POST public RestResponse<Example> create(Example example) { return RestResponse.ok(exampleService.create(example)); } } @Data public class Example { @JsonProperty("creationTime") private OffsetDateTime creationTime; // 前端传入2024-06-01T05:55:04 }
尝试过的无效配置
- 实现
ObjectMapperCustomizer
@Singleton public class ObjectMapperConfig implements ObjectMapperCustomizer { public void customize(ObjectMapper mapper) { var module = new SimpleModule(); module.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer()); mapper.registerModule(module); } static class CustomOffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); // 调试发现反序列化时未调用此方法 @Override public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctx) throws IOException { String dateString = p.getText(); try { return OffsetDateTime.parse(dateString); } catch (Exception e) { var localDateTime = LocalDateTime.parse(dateString, FORMATTER); return localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); } } } }
- 重写默认
ObjectMapperBean
@Produces @Singleton public ObjectMapper objectMapper() { var module = new SimpleModule(); module.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer()); var objectMapper = new ObjectMapper(); objectMapper.registerModule(module); return objectMapper; }
解决方案
方法一:替换JavaTimeModule中的默认反序列化器
Quarkus默认集成的JavaTimeModule内置了OffsetDateTime的反序列化器,直接添加自定义实现不会覆盖默认逻辑,需先移除默认处理器再添加自定义类:
@Singleton public class ObjectMapperConfig implements ObjectMapperCustomizer { @Override public void customize(ObjectMapper mapper) { // 获取已注册的JavaTimeModule,不存在则新建 JavaTimeModule javaTimeModule = mapper.getRegisteredModule(JavaTimeModule.class); if (javaTimeModule == null) { javaTimeModule = new JavaTimeModule(); mapper.registerModule(javaTimeModule); } // 移除默认的OffsetDateTime反序列化器 javaTimeModule.removeDeserializer(OffsetDateTime.class); // 添加自定义反序列化器 javaTimeModule.addDeserializer(OffsetDateTime.class, new CustomOffsetDateTimeDeserializer()); } static class CustomOffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); @Override public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctx) throws IOException { String dateString = p.getText().trim(); try { // 优先解析带时区偏移的标准格式 return OffsetDateTime.parse(dateString); } catch (DateTimeParseException e) { // 解析不带偏移的格式,转换为系统默认时区的OffsetDateTime LocalDateTime localDateTime = LocalDateTime.parse(dateString, FORMATTER); return localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); } } } }
方法二:字段级直接指定反序列化器
若仅需特定字段生效,可跳过全局配置,直接在实体类字段上添加注解:
@Data public class Example { @JsonProperty("creationTime") @JsonDeserialize(using = CustomOffsetDateTimeDeserializer.class) private OffsetDateTime creationTime; } // 独立的自定义反序列化器类 public class CustomOffsetDateTimeDeserializer extends JsonDeserializer<OffsetDateTime> { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); @Override public OffsetDateTime deserialize(JsonParser p, DeserializationContext ctx) throws IOException { String dateString = p.getText().trim(); try { return OffsetDateTime.parse(dateString); } catch (DateTimeParseException e) { LocalDateTime localDateTime = LocalDateTime.parse(dateString, FORMATTER); return localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); } } }
方法三:全局配置Jackson日期解析规则
无需编写自定义反序列化器,直接修改JavaTimeModule的解析逻辑:
@Singleton public class ObjectMapperConfig implements ObjectMapperCustomizer { @Override public void customize(ObjectMapper mapper) { JavaTimeModule javaTimeModule = new JavaTimeModule(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); // 替换OffsetDateTime的默认反序列化逻辑 javaTimeModule.addDeserializer(OffsetDateTime.class, new InstantDeserializer.OffsetDateTimeDeserializer( InstantDeserializer.OffsetDateTimeDeserializer.INSTANCE, formatter, (parser, context, f) -> { String str = parser.getText().trim(); try { return OffsetDateTime.parse(str, f); } catch (DateTimeParseException e) { LocalDateTime localDateTime = LocalDateTime.parse(str, f); return localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(localDateTime)); } } )); mapper.registerModule(javaTimeModule); // 关闭未知属性的严格校验,避免额外报错 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } }
关键说明
- Quarkus默认启用
JavaTimeModule,该模块的内置反序列化器优先级高于自定义模块,必须显式移除默认实现才能生效。 - 全局配置类需添加
@Singleton注解,确保被Quarkus扫描并加载。 - 调试时可开启Jackson日志:
quarkus.log.category."com.fasterxml.jackson".level=DEBUG,查看具体反序列化错误信息。
内容的提问来源于stack exchange,提问作者Politechniczny
相关产品推荐
相关产品推荐

