Search code examples
elasticsearchjacksonspring-data-elasticsearchelasticsearch-java-api

Elasticsearch Java Client Jackson Mapper pollutes Spring Jackson Mapper


I'm updating my Spring Boot project to version 3.0.0 of Spring Boot. One important dependecy is the Spring Data Elastic Repository. I included the starter dependency:

implementation("org.springframework.boot:spring-boot-starter-data-elasticsearch")

After updating the dependecies, my unit test are failing, because no more null values are provided by the web controller. I tried to change the behavior by using the property inclusion configuration:

spring:
  jackson:
    default-property-inclusion: always

This did not help. I debugged the code party and it seems like the new Elastic Java Client has it's own Jackson Mapper config, which sets the global config to 'NON_NULL'.

package co.elastic.clients.json.jackson;

...

public class JacksonJsonpMapper extends JsonpMapperBase {

    private final JacksonJsonProvider provider;
    private final ObjectMapper objectMapper;

   ...

    public JacksonJsonpMapper(ObjectMapper objectMapper) {
        this(
            objectMapper
                .configure(SerializationFeature.INDENT_OUTPUT, false)
                .setSerializationInclusion(JsonInclude.Include.NON_NULL),
            // Creating the json factory from the mapper ensures it will be returned by JsonParser.getCodec()
            new JacksonJsonProvider(objectMapper.getFactory())
        );
    }

...

Does anybody know how to solve this issue?


Solution

  • I found a soultion for the problem. Spring Boot has an autoconfiguration for the Elastic Jackson mapper. This autocofiguration uses the same ObjectMapper as the web controller:

    ´´´

    package org.springframework.boot.autoconfigure.elasticsearch;
    
    ...
    
    /**
     * Configurations for import into {@link ElasticsearchClientAutoConfiguration}.
     *
     * @author Andy Wilkinson
     */
    class ElasticsearchClientConfigurations {
    
    @ConditionalOnMissingBean(JsonpMapper.class)
    @ConditionalOnBean(ObjectMapper.class)
    @Configuration(proxyBeanMethods = false)
    static class JacksonJsonpMapperConfiguration {
    
        @Bean
        JacksonJsonpMapper jacksonJsonpMapper(ObjectMapper objectMapper) {
            return new JacksonJsonpMapper(objectMapper);
        }
    
    }
    

    ...

    ´´´

    It is possible to override this Bean to copy the ObjectMapper instance and create an own ObjectMapper for the Elastic client:

    ´´´

    @Configuration
    internal class CustomJacksonJsonpMapperConfiguration {
    @Bean
    fun jacksonJsonpMapper(objectMapper: ObjectMapper): JacksonJsonpMapper {
    
        return JacksonJsonpMapper(objectMapper.copy())
    }
    }
    

    ´´´