web-dev-qa-db-fra.com

Impossible de construire une instance de `Java.time.LocalDate` - Spring boot, elasticsearch, jackson

J'utilise Spring-boot 2.0.0.M7 et spring-boot-starter-data-elasticsearch et elasticsearch 5 et je reçois une erreur en désérialisant un champ LocalDate

Mon document ressemble à ça:

@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;
}

Où ClusterVisible est un objet enfant contenant la variable 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;
}

Donc, je viens de faire une requête pour un ID de cluster et je reçois cette 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)

Je sais déjà que je dois ajouter des dépendances jackson pour le Java.time api et j'ai donc ajouté:

<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>

Mais cela n'aide pas pour l'instant. J'ai également vérifié l'entrée dans l'index en effectuant une requête à l'aide de kibana. Le résultat de la requête est:

...
"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"
      }
    }
}

Qu'est-ce qui me manque pour corriger cette erreur? 

Ajout: L'erreur exacte se produit à mapper.mapToObject. J'ai donc créé un new DefaultEntityMapper(); quelques lignes auparavant. Cela pourrait-il être le problème?

@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);
}
8
Patrick

Le format de date/heure , conformément à la norme ISO 8601, est "AAAA-MM-JJ" , votre motif doit donc être:

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

Au lieu de:

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

Une autre façon consiste à ajouter dans votre application.yml

spring:
    jackson:
        serialization:
            WRITE_DATES_AS_TIMESTAMPS: false

Ou désactivez cette fonctionnalité directement dans votre mappeur d'objets:

objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
2
evdelacruz