LocalDate 反序列化和序列化错误 -jackson.databind.exc.InvalidDefinitionException:无法构造 java.time.LocalDate 的实例

Aak*_*rma 1 spring jackson elasticsearch spring-boot spring-data-elasticsearch

存储在 Elastic 中的员工的出生日期似乎存储为不正确的数据类型。我使用对象映射器和注册的 JavaTime 模块为 Elasticseach 客户端设置了代码。我正在尝试从 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
Run Code Online (Sandbox Code Playgroud)

构建.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'

Run Code Online (Sandbox Code Playgroud)

应用程序.yml

jackson:
    serialization:
         WRITE_DATES_AS_TIMESTAMPS: false
 jpa: 

    open-in-view: false
Run Code Online (Sandbox Code Playgroud)

弹性配置代码:

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

}
Run Code Online (Sandbox Code Playgroud)

错误:

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
Run Code Online (Sandbox Code Playgroud)

员工:

public class Employee extends AbstractAuditingEntity implements Serializable{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private Long id;

    //Remaining Fields are removed for brevity  

Run Code Online (Sandbox Code Playgroud)

抽象审计实体

@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();
Run Code Online (Sandbox Code Playgroud)

Ana*_*han 5

您的问题可能是因为 Jackson 不支持开箱即用的 Java 8 DateTime Api。所以你可以添加以下库来添加支持(在 pom.xml 中添加):

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.4.0</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)

并配置对象映射器以使用此库模块,例如:

ObjectMapper mapper = new ObjectMapper()
   .registerModule(new ParameterNamesModule())
   .registerModule(new Jdk8Module())
   .registerModule(new JavaTimeModule()); 
Run Code Online (Sandbox Code Playgroud)

使用链接供参考:https : //github.com/FasterXML/jackson-modules-java8