Ошибка десериализации и сериализации LocalDate -jackson.databind.exc.InvalidDefinitionException: Невозможно создать экземпляр java.time.LocalDate - PullRequest
1 голос
/ 30 мая 2019

Дата рождения сотрудника, хранящаяся в Elastic, похоже, хранится как неправильный тип данных. У меня есть код установки для клиента Elasticseach с сопоставителем объектов и зарегистрированным модулем JavaTime. Я пытаюсь получить список сотрудников из Elasticsearch, и он, похоже, не работает и не удаляется при десериализации.

Jar Versions:
org.elasticsearch.client:elasticsearch-rest-client -> 6.4.3 (*)
org.elasticsearch.client:elasticsearch-rest-high-level-client -> 6.4.3
org.elasticsearch:elasticsearch -> 6.4.3
org.springframework.data:spring-data-elasticsearch -> 3.1.4.RELEASE
com.fasterxml.jackson.datatype:jackson-datatype-hppc -> 2.9.8
com.fasterxml.jackson.core:jackson-core:2.9.8
com.fasterxml.jackson.core:jackson-databind:2.9.8 (*)
com.fasterxml.jackson.datatype:jackson-datatype-jsr310 -> 2.9.8 (*)
com.fasterxml.jackson.datatype:jackson-datatype-hibernate5 -> 2.9.8
com.fasterxml.jackson.core:jackson-annotations -> 2.9.0
com.fasterxml.jackson.module:jackson-module-afterburner -> 2.9.8
com.fasterxml.jackson.module:jackson-module-parameter-names:2.9.8 (*)
com.fasterxml.jackson.module:jackson-modules-java8:2.9.8

build.gradle:

compile "com.fasterxml.jackson.datatype:jackson-datatype-hppc"
    compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310"
    compile "com.fasterxml.jackson.datatype:jackson-datatype-hibernate5"
    compile "com.fasterxml.jackson.core:jackson-annotations"
    compile "com.fasterxml.jackson.core:jackson-databind"
    compile "com.fasterxml.jackson.module:jackson-module-afterburner"
    runtime group: 'com.fasterxml.jackson.module', name: 'jackson-modules-java8', version: '2.9.8', ext: 'pom'
    // https://mvnrepository.com/artifact/com.fasterxml.jackson.module/jackson-module-parameter-names
compile group: 'com.fasterxml.jackson.module', name: 'jackson-module-parameter-names', version: '2.9.8'

compile 'org.elasticsearch:elasticsearch'
    compile 'org.elasticsearch.client:transport'
    compile 'org.elasticsearch.client:elasticsearch-rest-client'
    compile 'org.elasticsearch.client:elasticsearch-rest-high-level-client'

    compile 'org.springframework.data:spring-data-elasticsearch'

application.yml

jackson:
    serialization:
         WRITE_DATES_AS_TIMESTAMPS: false
 jpa: 

    open-in-view: false

Код ElasticConfiguration:

@AutoConfigureAfter(value = { JacksonConfiguration.class })
public class ElasticsearchConfiguration {



    RestHighLevelClient client = null; 

    @Bean
    public RestHighLevelClient buildClient() {
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("localhost", 9200, "http"),
                        new HttpHost("localhost", 9201, "http")));
    }


    @Bean
    @Primary
    public ElasticsearchOperations elasticsearchTemplate(final ElasticsearchConverter elasticsearchConverter,
            EntityMapper mapper, Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder ) {
        return new ElasticsearchTemplate((Client) buildClient(), new CustomEntityMapper(new ObjectMapper()));
    }
    //jackson2ObjectMapperBuilder.createXmlMapper(false).build()


    public class CustomEntityMapper implements EntityMapper {

        private ObjectMapper objectMapper;

        public CustomEntityMapper(ObjectMapper objectMapper) {
            this.objectMapper = objectMapper;
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
            objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
            //objectMapper.registerModule(new JavaTimeModule());
            JavaTimeModule javaTimeModule=new JavaTimeModule();
            // Hack time module to allow 'Z' at the end of string (i.e. javascript json's) 
            javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE_TIME));
            javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_DATE));
            objectMapper.registerModule(javaTimeModule);
            objectMapper.registerModule(new ParameterNamesModule());
            objectMapper.registerModule(new Jdk8Module());
            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);
        }
    }

}

Ошибка:

Caused by: org.springframework.data.elasticsearch.ElasticsearchException: failed to map source [ {"id":73,"name":"K****","birthDate":{"year":2019,"month":"MAY","dayOfYear":150,"leapYear":false,"dayOfMonth":30,"dayOfWeek":"THURSDAY","era":"CE","monthValue":5,"chronology":{"id":"ISO","calendarType":"iso8601"}},"role..}] to class Employee
    at org.springframework.data.elasticsearch.core.AbstractResultMapper.mapEntity(AbstractResultMapper.java:45) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
    at org.springframework.data.elasticsearch.core.DefaultResultMapper.mapResults(DefaultResultMapper.java:102) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
    at org.springframework.data.elasticsearch.core.ElasticsearchTemplate.queryForPage(ElasticsearchTemplate.java:315) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
    at org.springframework.data.elasticsearch.core.ElasticsearchTemplate.queryForPage(ElasticsearchTemplate.java:309) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
    at org.springframework.data.elasticsearch.core.ElasticsearchTemplate.queryForPage(ElasticsearchTemplate.java:139) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
    at org.springframework.data.elasticsearch.repository.support.AbstractElasticsearchRepository.findAll(AbstractElasticsearchRepository.java:126) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
    at org.springframework.data.elasticsearch.repository.support.AbstractElasticsearchRepository.findAll(AbstractElasticsearchRepository.java:120) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_211]
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_211]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:1.8.0_211]
    at java.lang.reflect.Method.invoke(Unknown Source) ~[na:1.8.0_211]
    at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.1.4.RELEASE.jar:2.1.4.RELEASE]
Caused by: 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":73,"name":"Kelly","birthDate":{"year":2019,"month":"MAY","dayOfYear":150,"leapYear":false,"dayOfMonth":30,"dayOfWeek":"THURSDAY","era":"CE","monthValue":5,"chronology":{"id":"ISO","calendarType":"iso8601"}},"role...}},"lastModifiedBy""[truncated 396 chars]; line: 1, column: 38] (through reference chain: Employee["birthDate"])
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1028) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:127) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013) ~[jackson-databind-2.9.8.jar:2.9.8]
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004) ~[jackson-databind-2.9.8.jar:2.9.8]
    at org.springframework.data.elasticsearch.core.DefaultEntityMapper.mapToObject(DefaultEntityMapper.java:82) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
    at org.springframework.data.elasticsearch.core.AbstractResultMapper.mapEntity(AbstractResultMapper.java:43) ~[spring-data-elasticsearch-3.1.4.RELEASE.jar:3.1.4.RELEASE]
    ... 34 common frames omitted

Сотрудник:

public class Employee extends AbstractAuditingEntity implements Serializable{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;

    //Remaining Fields are removed for brevity  

AbstractAuditEntity

@MappedSuperclass
@Audited
@EntityListeners({AuditingEntityListener.class, EntityAuditEventListener.class})
public abstract class AbstractAuditingEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @CreatedBy
    @Column(name = "created_by", nullable = true, length = 50, updatable = false)
    private String createdBy;

    @CreatedDate
    @Column(name = "created_date")
    private LocalDateTime createdDate = LocalDateTime.now();

    @LastModifiedBy
    @Column(name = "last_modified_by", length = 50)
    private String lastModifiedBy;

    @LastModifiedDate
    @Column(name = "last_modified_date")
    private LocalDateTime lastModifiedDate = LocalDateTime.now();

Ответы [ 3 ]

0 голосов
/ 06 июня 2019

Мне удалось использовать типы java.time в Spring Data Elasticsearch со следующей конфигурацией:

@Configuration
@EnableElasticsearchRepositories(basePackages = "org.example.search.repository")
public class ElasticsearchConfig {

    @Bean
    public ElasticsearchTemplate elasticsearchTemplate(Client client) {
        return new ElasticsearchTemplate(client, new CustomEntityMapper());
    }

    static class CustomEntityMapper implements EntityMapper {

        private final ObjectMapper mapper;

        CustomEntityMapper() {
            mapper = new ObjectMapper()
                    .setDefaultPropertyInclusion(Include.NON_NULL)
                    .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                    .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                    .enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
                    .findAndRegisterModules();
        }

        @Override
        public String mapToString(Object object) throws IOException {
            return mapper.writeValueAsString(object);
        }

        @Override
        public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
            return mapper.readValue(source, clazz);
        }
    }
}
0 голосов
/ 13 июня 2019

Это скорее обходной путь, но с добавлением аннотации, упомянутой ниже, над соответствующими полями localdate / localdatetime.

@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)


0 голосов
/ 31 мая 2019

Возможно, ваша проблема будет в том, что Джексон не поддерживает Java 8 DateTime Api из коробки. Таким образом, вы можете добавить следующую библиотеку, чтобы добавить поддержку для этого (добавить в pom.xml):

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.4.0</version>
</dependency>

и настройте объектный маппер для использования этого библиотечного модуля, например:

ObjectMapper mapper = new ObjectMapper()
   .registerModule(new ParameterNamesModule())
   .registerModule(new Jdk8Module())
   .registerModule(new JavaTimeModule()); 

Используйте ссылку для справки: https://github.com/FasterXML/jackson-modules-java8

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...