You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
解决方案

核心原因

  1. 未覆盖所有时间字段:sunrise、sunset、dawn、dusk这些字段没有添加@JsonFormat注解,导致使用默认格式解析美式时间失败。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 02:35:57