【问题标题】:How to serialize a Date as LocalDate in Jackson?如何在杰克逊中将日期序列化为 LocalDate?
【发布时间】:2021-05-27 09:10:20
【问题描述】:

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

public class MyClass {
    private LocalDate myDate;
}

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

https://stackoverflow.com/a/38731094/10850340 (DateTimeFormatter.ISO_LOCAL_DATE) 中所示,可以创建 LocalDateSerializer

在我的场景中,接收数据的来源有多个。其中一个发送日期为 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: 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

的最佳方式是什么

【问题讨论】:

  • 如果字符串是2012-04-15T22:05:43Z,那么在我的时区已经是 4 月 16 日,所以不是可以从字符串中读取的日期。在这种情况下,您想要哪个日期?
  • 我明白了,我没有考虑过这种情况。在我的情况下,它将是字符串的日期;日期由只选择日期而不是时间的用户输入。但随后接收到的数据带有不相关的时间信息。

标签: java json jackson json-deserialization localdate


【解决方案1】:

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

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

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);
}

【讨论】:

    【解决方案2】:

    如果您只需要将日期从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 实例之前在映射器方法本身中处理它,而不是在反序列化器中进行转换,因为您有多个源使用多种日期时间格式。否则,您将不得不处理反序列化程序中的条件以处理您将接收日期的不同格式。如果您在实际将数据设置为实体的映射器方法中处理它(考虑到您有一个 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;
    }
    

    【讨论】:

      【解决方案3】:

      格式的可选部分

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

      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
      

      【讨论】:

        猜你喜欢
        • 2017-11-09
        • 2020-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-25
        • 2019-08-04
        相关资源
        最近更新 更多