Micronaut Serialization使用@JsonFormat反序列化LocalTime无效问题
问题:Micronaut Serialization解析美式时间到LocalTime失败
使用Micronaut 4.2.4搭配Micronaut Serialization(未引入Jackson),尝试将JSON中的美式时间(如sunrise、sunset)反序列化为java.time.LocalTime时抛出解析异常,即使已添加@JsonFormat指定格式和区域。
JSON数据
{ "results": { "date": "2024-02-02", "sunrise": "7:52:59 AM", "sunset": "5:27:56 PM", "first_light": "6:07:12 AM", "last_light": "7:13:44 PM", "dawn": "7:20:03 AM", "dusk": "6:00:52 PM", "solar_noon": "12:40:28 PM", "golden_hour": "4:41:39 PM", "day_length": "9:34:56", "timezone": "Europe/Zurich", "utc_offset": 60 }, "status": "OK" }
Java实体定义
@Serdeable record EntityResponse(Result results, String status) {} @Serdeable record CollectionResponse(List<Result> results, String status) {} @Serdeable record Result( LocalDate date, LocalTime sunrise, LocalTime sunset, @JsonProperty("first_light") @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime firstLight, @JsonProperty("last_light") @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime lastLight, LocalTime dawn, LocalTime dusk, @JsonProperty("solar_noon") @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime solarNoon, @JsonProperty("golden_hour") @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime goldenHour, Duration dayLength, ZoneId timezone, ZoneOffset utcOffset) { public static final String TIME_FORMAT = "h:mm:ss a"; }
测试代码
@MicronautTest public class MyTest { @Inject private ObjectMapper objectMapper; @Test @DisplayName("Verify that the deserialization works") public void deserialize() throws IOException { // given final String json = """ { "results": { "date": "2024-02-02", "sunrise": "7:52:59 AM", "sunset": "5:27:56 PM", "first_light": "6:07:12 AM", "last_light": "7:13:44 PM", "dawn": "7:20:03 AM", "dusk": "6:00:52 PM", "solar_noon": "12:40:28 PM", "golden_hour": "4:41:39 PM", "day_length": "9:34:56", "timezone": "Europe/Zurich", "utc_offset": 60 }, "status": "OK" } """; // when SunriseSunsetClient.EntityResponse response = objectMapper.readValue(json, SunriseSunsetClient.EntityResponse.class); // then assertEquals("OK", response.status()); assertNotNull(response.results()); } }
异常信息
Caused by: java.time.format.DateTimeParseException: Text '7:52:59 AM' could not be parsed at index 0 at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2108) at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:2010) at io.micronaut.serde.support.serdes.DefaultFormattedTemporalSerde.deserialize(DefaultFormattedTemporalSerde.java:71) at io.micronaut.serde.support.serdes.DefaultFormattedTemporalSerde.deserialize(DefaultFormattedTemporalSerde.java:35) at io.micronaut.serde.Deserializer.deserializeNullable(Deserializer.java:85) at io.micronaut.serde.support.deserializers.DeserBean$DerProperty.deserializeAndSetConstructorValue(DeserBean.java:838) ... 34 more Suppressed: java.lang.NumberFormatException: Character : is neither a decimal digit number, decimal point, nor "e" notation exponential mark. at java.base/java.math.BigDecimal.<init>(BigDecimal.java:608) at java.base/java.math.BigDecimal.<init>(BigDecimal.java:497) at java.base/java.math.BigDecimal.<init>(BigDecimal.java:903) at io.micronaut.serde.support.serdes.NumericSupportTemporalSerde.deserializeFallback(NumericSupportTemporalSerde.java:62) at io.micronaut.serde.support.serdes.DefaultFormattedTemporalSerde.deserialize(DefaultFormattedTemporalSerde.java:73)
解决方案
核心原因
- 未覆盖所有时间字段:
sunrise、sunset、dawn、dusk这些字段没有添加@JsonFormat注解,导致使用默认格式解析美式时间失败。 - Jackson注解兼容性限制:Micronaut Serialization对Jackson的
@JsonFormat注解支持不完全,尤其是locale参数在处理LocalTime时可能存在适配问题。
解决步骤
方法1:补全所有时间字段的Jackson注解
给所有美式时间字段添加@JsonFormat注解,确保统一指定解析格式和区域:
@Serdeable record Result( LocalDate date, @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime sunrise, @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime sunset, @JsonProperty("first_light") @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime firstLight, @JsonProperty("last_light") @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime lastLight, @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime dawn, @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime dusk, @JsonProperty("solar_noon") @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime solarNoon, @JsonProperty("golden_hour") @JsonFormat(pattern = TIME_FORMAT, locale = "en_US") LocalTime goldenHour, Duration dayLength, ZoneId timezone, ZoneOffset utcOffset) { public static final String TIME_FORMAT = "h:mm:ss a"; }
方法2:改用Micronaut原生注解(推荐)
使用Micronaut Serialization自带的@Format注解替代Jackson的@JsonFormat,原生注解对序列化器支持更完善:
import io.micronaut.serde.annotation.Format; @Serdeable record Result( LocalDate date, @Format(pattern = "h:mm:ss a", locale = "en_US") LocalTime sunrise, @Format(pattern = "h:mm:ss a", locale = "en_US") LocalTime sunset, @JsonProperty("first_light") @Format(pattern = "h:mm:ss a", locale = "en_US") LocalTime firstLight, @JsonProperty("last_light") @Format(pattern = "h:mm:ss a", locale = "en_US") LocalTime lastLight, @Format(pattern = "h:mm:ss a", locale = "en_US") LocalTime dawn, @Format(pattern = "h:mm:ss a", locale = "en_US") LocalTime dusk, @JsonProperty("solar_noon") @Format(pattern = "h:mm:ss a", locale = "en_US") LocalTime solarNoon, @JsonProperty("golden_hour") @Format(pattern = "h:mm:ss a", locale = "en_US") LocalTime goldenHour, Duration dayLength, ZoneId timezone, ZoneOffset utcOffset) {}
方法3:验证ObjectMapper类型
确保注入的ObjectMapper是Micronaut Serialization的实现(io.micronaut.serde.ObjectMapper),而非Jackson的com.fasterxml.jackson.databind.ObjectMapper,避免类型混淆导致的解析逻辑错误。
内容的提问来源于stack exchange,提问作者saw303
相关产品推荐
相关产品推荐

