问题描述
我们在datefield字段中填充了Elasticsearch索引中的 long
。
We have a date field being populated with a long
in elasticsearch index.
字段映射为:
@Field(type = FieldType.Date)
@JsonFormat(shape = JsonFormat.Shape.NUMBER_INT)
private LocalDateTime created;
然后我使用 Jackson
JavaTimeModule
和 Jdk8Module
具有以下配置:
And I use Jackson
JavaTimeModule
and Jdk8Module
with this configuration:
@Bean
public ElasticsearchOperations elasticsearchTemplate() {
return new ElasticsearchRestTemplate(client(), new CustomEntityMapper());
}
public static class CustomEntityMapper implements EntityMapper {
private final ObjectMapper objectMapper;
public CustomEntityMapper() {
//we use this so that Elasticsearch understands LocalDate and LocalDateTime objects
objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true)
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
//MUST be registered BEFORE calling findAndRegisterModules
.registerModule(new JavaTimeModule())
.registerModule(new Jdk8Module());
//only autodetect fields and ignore getters and setters for nonexistent fields when serializing/deserializing
objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
//load the other available modules as well
objectMapper.findAndRegisterModules();
}
@Override
public String mapToString(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}
@Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
return objectMapper.readValue(source, clazz);
}
}
但是当我尝试解析索引中的实体时字段如下:
But when I try to parse an entity in the index with a field such as:
"created" : 1563448935000
我得到一个错误:
com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_INT), expected VALUE_STRING: Expected array or string.
我认为,可以反序列化 long
约会,但我看不到丢失的内容。
I think, it is possible to deserialize a long
to a date, but I don't see what I am missing.
如果我将其映射为 Long
它当然可以工作,并且如果值存储为 String
也是一样的,我们将其整形并正确格式化为 @JsonFormat
。但是是否也可以具有 long-> LocalDateTime
?
If I map it to Long
it works of course and same if the value is stored as String
and we shape it and format properly in @JsonFormat
. But is it possible to have long->LocalDateTime
as well?
推荐答案
要从 1970-01-01T00:00:00Z
的纪元毫秒起构建 LocalDateTime
区。在版本中,它在以下情况时引发异常出现的毫秒数:
To build LocalDateTime
from milliseconds from the epoch of 1970-01-01T00:00:00Z
we need a time zone. In version 2.9.9 it throws exception when milliseconds appears:
我们可以实现我们的解串器,该解串器将尝试在默认时区执行此操作。示例实现如下所示:
But we can implement our deserialiser which will try to do this with default time zone. Example implementation could look like below:
class MillisOrLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
public MillisOrLocalDateTimeDeserializer() {
super(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
@Override
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
if (parser.hasToken(JsonToken.VALUE_NUMBER_INT)) {
long value = parser.getValueAsLong();
Instant instant = Instant.ofEpochMilli(value);
return LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
}
return super.deserialize(parser, context);
}
}
ZoneOffset .UTC
。您可以根据自己的意愿提供或使用系统默认值。用法示例:
ZoneOffset.UTC
is used. In your case you can provide yours or use system default. Example usage:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
public class JsonApp {
public static void main(String[] args) throws Exception {
JavaTimeModule javaTimeModule = new JavaTimeModule();
// override default
javaTimeModule.addDeserializer(LocalDateTime.class, new MillisOrLocalDateTimeDeserializer());
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(javaTimeModule);
String json = "{\"created\":1563448935000}";
System.out.println(mapper.readValue(json, Created.class));
}
}
class Created {
private LocalDateTime created;
// getters, setters, toString
}
以上代码打印出来:
Created{created=2019-07-18T11:22:15}
编辑:使用 Jackson 2.9.0
,原因是问题由于 findAndRegisterModules $ c $而不会调用提供的代码c>在注册自定义模块之后称为覆盖。删除该呼叫将使整个方案起作用。如果上述方法不适用于您的版本,则需要调试默认实现并找到原因。
Using Jackson 2.9.0
, because of this issue the code provided will not be invoked since findAndRegisterModules
which is called AFTER registering the customized module will override it. Removing that call will make the full scenario work. If above will not work for your version, you need to debug default implementation and find a reason.
这篇关于杰克逊使用Java 8将Elasticsearch反序列化为LocalDateTime的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!