如何在 Jackson 中将日期序列化为 LocalDate?

问题描述

我有一个字段类型为 LocalDate 的类:

public class MyClass {
    private LocalDate myDate;
}

我必须将值存储为 yyyy-MM-dd 而不是 [yyyy,M,d]

这可以通过创建一个 LocalDateSerializer 来完成,如 https://stackoverflow.com/a/38731094/10850340 (DateTimeFormatter.ISO_LOCAL_DATE) 中所示

在我的场景中,接收数据的来源有多个。其中之一将日期发送为 ISO_INSTANT '2011-12-03T10:15:30Z'。 LocalDateDeserializer

public class LocalDateDeserializer extends StdDeserializer<LocalDate> {

    private static final long serialVersionUID = 1L;
    
    protected LocalDateDeserializer() {
        super(LocalDate.class);
    }
    
    @Override
    public LocalDate deserialize(JsonParser jp,DeserializationContext ctxt)
            throws IOException,JsonProcessingException {
        return LocalDate.parse(jp.readValueAs(String.class));
    }

}

抛出以下警告:

解决 [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Text '2021-02-25T00:52:00.000Z' Could not be parsed,unparsed text found at index 10;嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException:无法解析文本 '2021-02-25T00:52:00.000Z',在索引 10 处找到未解析的文本(通过引用链:MyClass["myDate"])]

我的理解是接收到的字符串不能解析为 LocalDate: LocalDate.parse(jp.readValueAs(String.class));

LocalDateDeserializer 接受任何有效日期格式但返回 LocalDate

的最佳方法是什么?

提前致谢!

解决方法

如果您只需要将日期从 Date 对象转换为 LocalDate,则在填充 MyClass 实例时:

Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

java.util.Date 代表时间线上的一个瞬间,而不是“日期”。

但是在 Java 9 中,您可以执行以下操作:

LocalDate date = LocalDate.ofInstant(input.toInstant(),ZoneId.systemDefault());

不是在反序列化器中进行转换,您可以在将值设置为 MyClass 实例之前在映射器方法本身中处理它,因为您有多个具有多种日期时间格式的源正在使用。否则,您将不得不处理 de 序列化程序中的条件,以处理您将要接收日期的不同格式。如果您在实际将数据设置到实体的映射器方法中处理它(考虑到您有一个 DTO 类),您可以为来自不同来源的不同日期格式提供特定实现。

如果您仍想使用解串器,请对不同的日期格式使用 DateTimeFormatter 并进行相应设置:

@Override
public LocalDate deserialize(JsonParser jp,DeserializationContext ctxt)
        throws IOException,JsonProcessingException {

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd");

    if (**somecondition**) {
       dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd");
    }
    
    LocalDate dt = dtf.parseLocalDate(yourinput);
    return dt;
}
,

格式的可选部分

您可以在格式模式字符串中使用可选部分。这用方括号括起来,表示在解析过程中可能存在或不存在的日期时间字符串的一部分。像这样:

private static final DateTimeFormatter inputFormatter
        = DateTimeFormatter.ofPattern("uuuu-MM-dd['T'HH:mm:ssX]");

如果您确定附加不相关的时间和偏移量(Z 是 UTC 偏移量)没有改变日期,您现在可以这样解析:

    for (String inputString
            : new String[] { "2011-12-03T10:15:30Z","2013-11-21" }) {
        LocalDate date = LocalDate.parse(inputString,inputFormatter);
        System.out.format("%-20s parsed to %s%n",inputString,date);
    }

输出:

2011-12-03T10:15:30Z parsed to 2011-12-03
2013-11-21           parsed to 2013-11-21
,

对于我的特定场景,我知道只有两种格式可以输入日期。我可以检查它是哪种格式并进行相应处理。

我发布了在我的场景中有效的方法,但我确信还有更多“稳健”的解决方案。

booktabs = TRUE