5

我有以下 JSON:

{
      "id" : "1",
      "birthday" : 401280850089
}

和 POJO 类:

public class FbProfile {
    long id;
    @JsonDeserialize(using = LocalDateDeserializer.class)
    LocalDate birthday;
}

我正在使用 Jackson 进行反序列化:

public FbProfile loadFbProfile(File file) throws JsonParseException, JsonMappingException, IOException {
    ObjectMapper mapper = new ObjectMapper();
    FbProfile profile = mapper.readValue(file, FbProfile.class);
    return profile;
}

但它抛出了一个异常:

com.fasterxml.jackson.databind.JsonMappingException:意外的令牌(VALUE_NUMBER_INT),预期的VALUE_STRING:预期的数组或字符串。

如何将纪元反序列化为LocalDate?我想补充一点,如果我将数据类型从 更改LocalDatejava.util.Date它工作得很好。所以也许最好反序列java.util.Date化并创建 getter 和 setter 来转换到/从LocalDate.

4

3 回答 3

5

我已经设法编写了自己的反序列化器(感谢@Ole VV 将我指向Java 8 LocalDate Jackson 格式的帖子):

public class LocalDateTimeFromEpochDeserializer extends StdDeserializer<LocalDateTime> {

    private static final long serialVersionUID = 1L;

    protected LocalDateTimeFromEpochDeserializer() {
        super(LocalDate.class);
    }

    @Override
    public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        return Instant.ofEpochMilli(jp.readValueAs(Long.class)).atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

}

关于时区的通知也非常有用。谢谢!

仍然悬而未决的问题是是否可以在不编写自己的反序列化器的情况下完成?

于 2017-06-08T08:33:04.737 回答
3

如果您有能力更改 POJO,我选择的另一个选择是将您的字段声明为java.time.Instant.

public class FbProfile {
    long id;
    Instant birthday;
}

这将从包括 epoch 在内的多种不同格式反序列化。然后,如果您需要LocalDate在业务逻辑中将其用作 a 或其他内容,只需执行上述某些转换器正在执行的操作:

LocalDate asDate = birthday.atZone(ZoneId.systemDefault()).toLocalDate()

或者

LocalDateTime asDateTime = birthday.atZone(ZoneId.systemDefault()).toLocalDateTime()
于 2018-06-08T13:40:27.957 回答
1

我找到了一种无需编写自定义反序列化器的方法,但需要进行一些修改。

首先,LocalDateDeserializer接受一个 custom DateTimeFormatter。因此,我们需要创建一个接受纪元毫秒的格式化程序。我通过加入INSTANT_SECONSandMILLI_OF_SECOND字段来做到这一点:

// formatter that accepts an epoch millis value
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    // epoch seconds
    .appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER)
    // milliseconds
    .appendValue(ChronoField.MILLI_OF_SECOND, 3)
    // create formatter, using UTC as timezone
    .toFormatter().withZone(ZoneOffset.UTC);

我还将格式化程序设置为 UTC 时区,因此它不会受到时区和 DST 更改的影响。

然后,我创建了反序列化器并在我的ObjectMapper

ObjectMapper mapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
// add the LocalDateDeserializer with the custom formatter
module.addDeserializer(LocalDate.class, new LocalDateDeserializer(formatter));
mapper.registerModule(module);

我还必须从birthday字段中删除注释(因为注释似乎覆盖了模块配置):

public class FbProfile {
    long id;

    // remove @JsonDeserialize annotation
    LocalDate birthday;
}

现在是一个大问题:由于DateTimeFormatter只接受String作为输入,并且 JSON 在birthday字段中包含一个数字,我不得不更改 JSON:

{
  "id" : "1",
  "birthday" : "401280850089"
}

请注意,我更改birthday为 a String(将值放在引号之间)。

有了这个,LocalDate从 JSON 中正确读取:

FbProfile value = mapper.readValue(json, FbProfile.class);
System.out.println(value.getBirthday()); // 1982-09-19

笔记:

  • 我找不到将数字直接传递给格式化程序的方法(因为它只需要String作为输入),所以我不得不将数字更改为String. 如果您不想这样做,那么无论如何您都必须编写自定义转换器。
  • 您可以替换ZoneOffset.UTC为您想要的任何时区(甚至ZoneId.systemDefault()),这取决于您的应用程序需要什么。但正如@Ole VV 的评论中所说,时区可能会导致日期发生变化。
于 2017-06-08T16:19:02.313 回答