Cannot construct instance of `java.time.LocalDate` - Spring boot, elasticseach, jackson

47,027

Solution 1

add below annotation over the field LocalDate

@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate start;

Solution 2

Date/time format, according to ISO 8601 is "YYYY-MM-DD", so your pattern should be:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")

Instead of:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd.MM.yyyy")

Another way is adding in your application.yml

spring:
    jackson:
        serialization:
            WRITE_DATES_AS_TIMESTAMPS: false

Or disable this feature directly in your object mapper:

objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)

Solution 3

need to install JsonFormat JsonDeserialize JsonSerialize

private LocalDate dateOfBirth;

    @PastOrPresent(message = "must be past time or present")
    @Column(name = "date_of_birth", nullable = false, columnDefinition = "DATE")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    public LocalDate getDateOfBirth() {
        return dateOfBirth;
    }

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
    public void setDateOfBirth(LocalDate dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }
Share:
47,027

Related videos on Youtube

Patrick
Author by

Patrick

Java and Spring Software Developer.

Updated on July 09, 2022

Comments

  • Patrick
    Patrick almost 2 years

    I'm using Spring-boot 2.0.0.M7 and spring-boot-starter-data-elasticsearch and elasticsearch 5 and I'm getting an error by deserializing a LocalDate field.

    My Document looks like that:

    @Document(indexName= "myIndex", type = "cluster")
    public class Cluster {
    
        @Id
        @Field
        private Long id;
        @Field
        private String name;
        @Field
        private ClusterUrl clusterUrl;
        @Field
        private ClusterVisible clusterVisible;
    }
    

    Where ClusterVisible is a child object which holds the LocalDates:

    public class ClusterVisible {
    
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd.MM.yyyy")
        private LocalDate start;
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd.MM.yyyy")
        private LocalDate end;
    }
    

    So I just make a query for one cluster Id and I get this exception:

    com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.LocalDate` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
     at [Source: (String)"{"id":12345,"name":"Cluster name ","clusterName":{"de":"Cluster de","it":null,"fr":null},"clusterUrl":{"de":"/url/results","it":null,"fr":null},"clusterVisible":{"start":{"year":2017,"month":"OCTOBER","dayOfMonth":9,"dayOfWeek":"MONDAY","era":"CE","dayOfYear":282,"leapYear":false,"mo"[truncated 252 chars]; line: 1, column: 388] (through reference chain: com.example.elasticsearch5.es.cluster.model.Cluster["clusterVisible"]->com.example.elasticsearch5.es.cluster.model.ClusterVisible["start"])
        at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
        at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1451)
        at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1027)
        at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1290)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
        at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
        at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
        at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
        at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4001)
        at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992)
        at org.springframework.data.elasticsearch.core.DefaultEntityMapper.mapToObject(DefaultEntityMapper.java:65)
    

    I already know that I need to add some jackson dependencies for the java.time api so I added:

    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.4</version>
    </dependency>
    

    But this does not help for now. I also checked the entry in the index by making a query using kibana. The result of the query is:

    ...
    "clusterVisible": {
        "start": {
          "year": 2017,
          "month": "OCTOBER",
          "dayOfMonth": 25,
          "dayOfWeek": "WEDNESDAY",
          "era": "CE",
          "dayOfYear": 298,
          "leapYear": false,
          "monthValue": 10,
          "chronology": {
            "id": "ISO",
            "calendarType": "iso8601"
          }
        },
        "end": {
          "year": 3000,
          "month": "JANUARY",
          "dayOfMonth": 1,
          "dayOfWeek": "WEDNESDAY",
          "era": "CE",
          "dayOfYear": 1,
          "leapYear": false,
          "monthValue": 1,
          "chronology": {
            "id": "ISO",
            "calendarType": "iso8601"
          }
        }
    }
    

    What do I miss for fixing this error?

    Addition: The exact error occours at mapper.mapToObject. So I created a new DefaultEntityMapper(); some lines before. Could that be the issue?

    @Override
    public Page<Cluster> findClustersAndScoreByText(String text) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .should(QueryBuilders.queryStringQuery(text).lenient(true).defaultOperator(Operator.OR)
                        .field("name")
                        .field("svno"));
    
        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder().withQuery(queryBuilder)
                .withPageable(PageRequest.of(0, 100)).build();
    
        DefaultEntityMapper mapper = new DefaultEntityMapper();
        ResultsExtractor<Page<Cluster>> rs = new ResultsExtractor<Page<Cluster>>() {
    
            @Override
            public Page<Cluster> extract(SearchResponse response) {
                ArrayList<Cluster> hotels = new ArrayList<>();
                SearchHit[] hits = response.getHits().getHits();
                for (SearchHit hit : hits) {
                    try {
                        Cluster cluster = mapper.mapToObject(hit.getSourceAsString(), Cluster.class);
                        cluster.setScore(hit.getScore());
                        hotels.add(cluster);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                return new PageImpl<>(hotels, PageRequest.of(0, 100), response.getHits().getTotalHits());
            }
        };
    
        return elasticsearchTemplate.query(nativeSearchQuery, rs);
    }
    
    • OneCricketeer
      OneCricketeer about 6 years
      Your start and end data are objects... Why would pattern = "dd.MM.yyyy" work?
    • Patrick
      Patrick about 6 years
      @cricket_007 should be ok for deserializing to the frontend. But I removed it and there is no change.
    • OneCricketeer
      OneCricketeer about 6 years
      The error is telling you that no default constructor exists for a localdatetime and its an immutable class, so it can't just call setters for each field in the json. Why don't you write your own class for that object?
    • Patrick
      Patrick about 6 years
      @cricket_007 if the config does not work I need to write my own class. I autowired the jackson object mapper instead using a new one, I get another exception for localdate: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected VALUE_STRING: Expected array or string. any idea?
    • OneCricketeer
      OneCricketeer about 6 years
      "start": { is an object, as the error says. JsonFormat.Shape.STRING is trying to parse a string, which you don't have
  • user1428716
    user1428716 over 4 years
    this solution would not work if these are auto-generated stubs. Answer by @evdelacruz is more valid
  • Dimitri Kopriwa
    Dimitri Kopriwa about 3 years
    How can I disable the serialization for one request and not all of them?
  • evdelacruz
    evdelacruz about 3 years
    @DimitriKopriwa, the ObjectMapper is a bean that is built within the ApplicationContext to be injected as needed. I'm afraid u will need to use Jackson custom annotations to achieve that. Another possibility is to treat the field as a String within the DTO/VO/TO and then parse it as desired within that specific scenario/request.
  • Irinel
    Irinel about 2 years
    it's not enough