我有以下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
?我想补充一点,如果我将数据类型从LocalDate
更改为java.util.Date
,它的工作原理非常好。因此,最好将反序列化为java.util.Date
并创建将进行/ LocalDate
转换的getter和setter。
答案 0 :(得分:3)
我成功地编写了自己的反序列化程序(感谢@Ole V.V.指出我的帖子Java 8 LocalDate Jackson format):
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();
}
}
关于时区的注意事项也非常有用。谢谢!
仍然存在的问题是,如果可以在不编写自己的反序列化器的情况下完成吗?
答案 1 :(得分:1)
我找到了一种方法,无需编写自定义反序列化器,但需要进行一些修改。
首先,LocalDateDeserializer
接受自定义DateTimeFormatter
。因此,我们需要创建一个接受epoch millis的格式化程序。我是通过加入INSTANT_SECONS
和MILLI_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
更改为String
(将值放在引号之间)。
有了这个,正确地从JSON读取LocalDate
:
FbProfile value = mapper.readValue(json, FbProfile.class);
System.out.println(value.getBirthday()); // 1982-09-19
备注:强>
String
作为输入),所以我不得不将数字更改为String
。如果您不想这样做,那么无论如何您都必须编写自定义转换器。ZoneOffset.UTC
替换为您想要的任何时区(甚至ZoneId.systemDefault()
),这取决于您的应用需求。但正如@Ole V.V.'s comment中所述,时区可能会导致日期发生变化。答案 2 :(得分:0)
如果您能够更改POJO,我选择的另一个选项是将您的字段声明为java.time.Instant
。
public class FbProfile {
long id;
Instant birthday;
}
这将从包括纪元在内的多种不同格式反序列化。然后,如果您需要将其用作业务逻辑中的LocalDate
或其他内容,只需执行上述某些转换器正在执行的操作:
LocalDate asDate = birthday.atZone(ZoneId.systemDefault()).toLocalDate()
或
LocalDateTime asDateTime = birthday.atZone(ZoneId.systemDefault()).toLocalDateTime()