杰克逊使用Java 8将Elasticsearch反序列化为LocalDateTime [英] Jackson deserialize elasticsearch long as LocalDateTime with Java 8

查看:374
本文介绍了杰克逊使用Java 8将Elasticsearch反序列化为LocalDateTime的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们在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 区。在版本 2.9.9 中,它在以下情况时引发异常出现的毫秒数:

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:


原始时间戳(1563448935000)不允许
java.time.LocalDateTime :需要其他信息,例如
偏移量或时区(请参见Javadocs类)

raw timestamp (1563448935000) not allowed for java.time.LocalDateTime: need additional information such as an offset or time-zone (see class Javadocs)

我们可以实现我们的解串器,该解串器将尝试在默认时区执行此操作。示例实现如下所示:

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 在注册自定义模块之后称为覆盖。删除该呼叫将使整个方案起作用。如果上述方法不适用于您的版本,则需要调试默认实现并找到原因。

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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆