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

How to serialize a Date as LocalDate in Jackson?

我有一个 class 字段类型 LocalDate:

public class MyClass {
    private LocalDate myDate;
}

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

这可以通过创建 LocalDateSerializer 来完成,如 (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));
    }

}

抛出以下警告:

Resolved [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; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Text '2021-02-25T00:52:00.000Z' could not be parsed, unparsed text found at index 10 (through reference chain: MyClass["myDate"])]

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

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

的最佳方法是什么

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

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

public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
    String input_date = jp.readValueAs(String.class);
    boolean is_date_time = input_date.contains("T");
    if (is_date_time) {
        int end = input_date.indexOf('T');
        input_date = input_date.substring(0, end);
    }
    return LocalDate.parse(input_date);
}

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

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 实例之前在映射器方法本身中处理它,因为您有多个来源使用了多种日期时间格式。否则,您将不得不处理反序列化程序中的条件,以处理您将要接收日期的不同格式。如果您在实际将数据设置为实体 的映射器方法中处理它(考虑到您有一个 DTO class),您可以为不同的日期提供具体的实现来自不同来源的格式。

如果您仍想使用反序列化器,请为不同的日期格式使用 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