【问题标题】:How to deserialize date field with many formats to LocalDate variable如何将具有多种格式的日期字段反序列化为 LocalDate 变量
【发布时间】:2021-07-17 20:45:53
【问题描述】:

我有一个简单的 DTO,我想以 JSON 格式接收它并使用 Jackson 的 ObjectMapper 反序列化

class SimpleDto {
    LocalDate date;
}

问题是这个日期的格式,然而应该是yyyy-MM-dd,是不固定的,它可能会遗漏一些部分——例如。它可以是完整的日期

{
    "date": "2020-01-01"
}

或者只是年有月无日

{
    "date": "2020-01"
}

甚至一年

{
    "date": "2020"
}

我正在尝试使用 ObjectMapper 和 JavaTimeModule 注册但它不支持例如年无月日格式

ObjectMapper mapper = new ObjectMapper().registerModule(JavaTimeModule());

是否可以以某种方式将其映射到LocalDate?我看到了一些类似的主题(如this),但似乎他们没有回答我的问题


编辑

现在我开始考虑我是否应该将此字段视为LocalDate - 据我了解,没有什么像LocalDate.of(2000, 0, 0)(实际上它正在引发异常)然后将1990反序列化为(什么?)@ 987654335@ 显然是一个错误,并且可能会导致逻辑中出现一些异常,该逻辑会假设 01 是有效且提供的月份,01 是有效且提供的日期

【问题讨论】:

    标签: java jackson localdate


    【解决方案1】:

    您可以创建自定义序列化程序,其中包含使用DateTimeFormatterBuilder#parseDefaulting 构建的DateTimeFormatter。

    import java.time.LocalDate;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeFormatterBuilder;
    import java.time.temporal.ChronoField;
    import java.util.Locale;
    import java.util.stream.Stream;
    
    public class Main {
        public static void main(String[] args) {
            //Test
            Stream.of(
                        "2020-01-01",
                        "2020-01",
                        "2020"
            ).forEach(s -> System.out.println(parseToLocalDate(s)));
        }
        static LocalDate parseToLocalDate(String date) {
            DateTimeFormatter dtf = new DateTimeFormatterBuilder()
                                        .appendPattern("u[-M[-d]]")
                                        .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
                                        .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
                                        .toFormatter(Locale.ENGLISH);
            
            return LocalDate.parse(date, dtf);
        }
    }
    

    输出:

    2020-01-01
    2020-01-01
    2020-01-01
    

    模式u[-M[-d]] 在方括号内有可选字段。

    【讨论】:

      【解决方案2】:

      如何扩展 LocalDateDeserializer 然后使用 @JsonSerialize(using = YourLocalDateSerializer.class)

      【讨论】:

      • 听起来像是可行的方法,但我想避免在每个 LocalDate 字段上添加注释 - 我有很多
      • 所有这些都可能以这些截断格式出现?最好将自定义反序列化程序的用户限制在实际需要的地方。
      【解决方案3】:

      LocalDate 只能保存一个有效日期。我从您的编辑中了解到,如果您的 JSON 输入中只有 2020 年,您不希望日期为 2020 年 1 月 1 日。您可以围绕以下代码构建自定义反序列化器,但它会有一些缺点。

      public TemporalAccessor deserialize(String inputDate) {
          switch (inputDate.length()) {
          case 10:
              return LocalDate.parse(inputDate);
          case 7:
              return YearMonth.parse(inputDate);
          case 4:
              return Year.parse(inputDate);
          default:
              throw new IllegalArgumentException("Not a valid input date: " + inputDate);
          }
      }
      

      不知道结果的确切类型是不切实际的,但可以管理。而且您将确定如果解析的值包括一个月或一个月中的一天,那么这些来自输入。演示:

          String[] exampleInputs = { "2020-01-01", "2020-01", "2020" };
          for (String example : exampleInputs) {
              TemporalAccessor parsed = deserialize(example);
              System.out.format(
                      "%-10s parsed into %-10s, a %-9s. Has month? %-5b Has day? %b%n",
                      example, parsed, parsed.getClass().getSimpleName(),
                      parsed.isSupported(ChronoField.MONTH_OF_YEAR),
                      parsed.isSupported(ChronoField.DAY_OF_MONTH));
          }
      

      输出是:

      2020-01-01 parsed into 2020-01-01, a LocalDate. Has month? true  Has day? true
      2020-01    parsed into 2020-01   , a YearMonth. Has month? true  Has day? false
      2020       parsed into 2020      , a Year     . Has month? false Has day? false
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-10-07
        • 2018-12-13
        • 2018-10-10
        • 2013-07-28
        • 2014-10-17
        • 1970-01-01
        相关资源
        最近更新 更多