Spring Data Elastic - Java.Time.Instant class Jackson deserliization not working
Asked Answered
T

2

0

Following is my WorkroomDTO:

  @NotNull
  private Instant createdOn;

  @JsonInclude(JsonInclude.Include.NON_NULL)
  private Instant changedOn;

As you can see i am using Java 8 Instant class. In the elasticsearch Index server i store the following as JSON:

  "createdOn": {
    "nano": 877000000,
    "epochSecond": 1579861613
  },
  "changedOn": {
    "nano": 920000000,
    "epochSecond": 1579861613
  },

The problem is when i query the elasticsearch server to get me the workroom

    return elasticsearchOperations.queryForPage(new NativeSearchQueryBuilder().withQuery(mainQuery)
                                                                          .withPageable(elasticUtils.interceptPageable(searchDto.getPageable(), "name"))
                                                                          .build(),
                                            WorkroomDTO.class);

, i make a mapping of these fields to my WorkroomDTO i get the following exception:

    com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.Instant` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{"createdOn":{"nano":68000000,"epochSecond":1580127683}

FYI:

I have created a configuration file where is register explicitly the JavaTimeModule to the Object Mapper

 @Configuration
public class JacksonConfiguration {

  @Value("${application.serialization.include-type-key:true}")
  private boolean includeTypeKey = true;

  @Bean
  public ObjectMapper objectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.addHandler(new MissingTypeIdHandler());
    if (includeTypeKey) {
      mapper.setMixInResolver(new TypeKeyMixInResolver());
    }
    return mapper;
  }

}

Need help!

Technic answered 17/2, 2020 at 14:31 Comment(0)
S
1

Where does this data come from? Is it written by your application?

It seems that the used Jackson mapper does not have the jackson-datatype-jsr310 module registered.

On reading the data tries to find a constructor of Instant that can be used to create an Instant object. But Instant does not have a default constructor and the Mapper should use the Instant.ofEpochSecond(long, long) method. This page pretty declares the problem and shows how the Jackson Mapper is configured.

Storing an instant in this way, as an object with two properties, is not the right way for storing dates in Elasticsearch. You should read the Elasticsearch documentation about how Elastcisearch handles date/time fields. When storing the instant as an object like this, you loose the ability to use Elasticsearch queries with criteria based on a date/time.

Which version of Spring Data Elasticsearch do you use? Because of problems like this, from the upcoming version 4.0 on, Spring Data Elasticsearch will not use the Jackson mapper anymore for entity mapping. The MappingElasticsearchConverter supports the use of the Elasticsearch date format and the java.time classes.

Sped answered 18/2, 2020 at 11:31 Comment(2)
Thank you for your answer and suggestions:Technic
Thank you for your answer and suggestions, the data is stored by my application to the elasticsearch indexing server. I have added jackson-datatype-jsr310 to my pom.xml and tried to register the module to the ObjectMapper. But still it is not able to map to the desired Object Workroom. I use Spring Data Elasticsearch 3.2.0..Technic
A
0

Well if I'm not completely wrong, your mapping fails due to the wrong format. The json you get looks like this:

 "createdOn": {
    "nano": 877000000,
    "epochSecond": 1579861613
  },
  "changedOn": {
    "nano": 920000000,
    "epochSecond": 1579861613
  },

That means you have 2 objects 'createdOn' and 'changedOn' with two properties (nano, epochSecond), while you try to map it to one object containing two properties named 'createdOn' and 'changedOn'. You need to modify that, you have e.g. a class called Entry, with two properties (nano, epochSeconds) and then a class with two properties (createdOn, changedOn) of type Entry

Austin answered 17/2, 2020 at 15:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.