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

ZonedDateTime无法从JSON反序列化,但测试格式化可正常解析

问题描述

通过JSON传入日期字符串"29-OCT-21 12.00.00.000000000 AM UTC",希望将其反序列化为ZonedDateTime类型,实体类配置如下:

@JsonProperty("createdTs")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MMM-yy hh.mm.ss.SSSSSSSSS a z", with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
private ZonedDateTime createdTs;

但出现反序列化错误:

org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.time.ZonedDateTime` from String "29-OCT-21 12.00.00.000000000 AM UTC": Failed to deserialize java.time.ZonedDateTime: (java.time.format.DateTimeParseException) Text '29-OCT-21 12.00.00.000000000 AM UTC' could not be parsed 

而相同格式的日期字符串,在测试用例中使用DateTimeFormatterBuilder手动解析可以正常运行:

DateTimeFormatter formatter = new DateTimeFormatterBuilder().parseCaseInsensitive().appendPattern("dd-MMM-yy hh.mm.ss.SSSSSSSSS a z").toFormatter(Locale.getDefault());
locationPayload.setcreatedTs(ZonedDateTime.parse("29-OCT-21 12.00.00.000000000 AM UTC", formatter));
问题原因
  1. Locale不匹配:手动解析时指定了Locale.getDefault(),但Jackson默认使用的Locale可能与测试环境不一致,导致月份缩写(OCT)无法被正确识别。
  2. 大小写不敏感配置无效:JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES仅用于忽略JSON属性名的大小写,而非日期字符串内容的大小写,因此日期中的大写月份缩写无法被Jackson默认解析器识别。
  3. 纳秒解析兼容性:Jackson默认的ZonedDateTime解析器对9位纳秒的处理逻辑,与手动构建的DateTimeFormatter存在差异。
解决方案

方案1:自定义Jackson日期反序列化器

在实体类字段上指定自定义反序列化器,直接复用测试用例中验证过的格式化逻辑:

@JsonProperty("createdTs")
@JsonDeserialize(using = CustomZonedDateTimeDeserializer.class)
private ZonedDateTime createdTs;

实现自定义反序列化器:

public class CustomZonedDateTimeDeserializer extends JsonDeserializer<ZonedDateTime> {
    private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendPattern("dd-MMM-yy hh.mm.ss.SSSSSSSSS a z")
            .toFormatter(Locale.getDefault());

    @Override
    public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        String dateStr = p.getText();
        return ZonedDateTime.parse(dateStr, FORMATTER);
    }
}

方案2:全局配置Jackson的日期格式化规则

通过配置类全局替换Jackson的ZonedDateTime解析逻辑,避免每个字段单独配置:

@Configuration
public class JacksonConfig {
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();

        DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .parseCaseInsensitive()
                .appendPattern("dd-MMM-yy hh.mm.ss.SSSSSSSSS a z")
                .toFormatter(Locale.getDefault());

        javaTimeModule.addDeserializer(ZonedDateTime.class, new JsonDeserializer<ZonedDateTime>() {
            @Override
            public ZonedDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
                return ZonedDateTime.parse(p.getText(), formatter);
            }
        });

        objectMapper.registerModule(javaTimeModule);
        return objectMapper;
    }
}

此时实体类的@JsonFormat注解可以保留,也可以去掉,因为全局序列化器已经覆盖了默认逻辑。

方案3:修正@JsonFormat配置

仅调整注解参数,补充Locale并使用Jackson的日期大小写不敏感特性(需Jackson 2.13+版本支持):

@JsonProperty("createdTs")
@JsonFormat(
    shape = JsonFormat.Shape.STRING,
    pattern = "dd-MMM-yy hh.mm.ss.SSSSSSSSS a z",
    locale = "en_US",
    with = {JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_VALUES}
)
private ZonedDateTime createdTs;

注意:ACCEPT_CASE_INSENSITIVE_VALUES是针对字段值的大小写不敏感,和原配置中的ACCEPT_CASE_INSENSITIVE_PROPERTIES(针对属性名)不是同一个参数。

内容的提问来源于stack exchange,提问作者Catherine S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 16:00:28