简体   繁体   中英

Jackson serialization is not working after Spring Boot upgrade

Yesterday I started with upgrade from Spring Brussels-SR3 to Spring Brussels-SR6.
The Spring Boot goes from 1.5.4. to 1.5.9, Jackson goes from 2.8.8 to 2.8.10). I am using HATEOAS and HAL links. It means my Jackson configuration looks like this:

@Configuration
public class JacksonConfiguration {

    private static final String SPRING_HATEOAS_OBJECT_MAPPER = "_halObjectMapper";

    @Autowired
    @Qualifier(SPRING_HATEOAS_OBJECT_MAPPER)
    private ObjectMapper springHateoasObjectMapper;

    @Primary
    @Bean(name = "objectMapper")
    ObjectMapper objectMapper() {
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer(DateTimeFormatter.ISO_INSTANT));

        springHateoasObjectMapper.registerModules(javaTimeModule);

        springHateoasObjectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        springHateoasObjectMapper.disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);

        springHateoasObjectMapper.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);
        springHateoasObjectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        springHateoasObjectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);

        springHateoasObjectMapper.enable(SerializationFeature.INDENT_OUTPUT);

        springHateoasObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        return springHateoasObjectMapper;
    }
}

It means I am reusing the _halObjectMapper bean and adding some more configurations. It had been working until I started with the upgrade.

What is wrong?

The thing that goes wrong is that after the upgrade all my serialization customizations and HAL conventions are not applied- datetime formats, indenting HAL "_links" JSON field changes to "links" ... So the _halObjectMapper is not used for serialization any more.

Any clue what could be the issue or where should I dig to figure out what is wrong?

Additional info after some debugging:

I have figured out that TypeConstrainedMappingJackson2HttpMessageConverter that uses _halObjectMapper is not used for conversion to json anymore. The reason is that it does not get to the collection of converters when starting spring. It looks like it is not created for RequestMappingHandlerAdapter bean because of some condition that skips creation when Jackson2HalModule is allready registered in some other converter (in this case ProjectingJackson2HttpMessageConverter).

Any idea what could be a cause or where to look at to figure out why the spring boot start proceeds differently?

Additional info after some more debugging:

The difference I see before and after the upgrade is that before the upgrade the ProjectingJackson2HttpMessageConverter was populated with new instance of ObjectMapper. But after the upgrade, the ObjectMapper is resolved from container so the _halObjectMapper is chosen. As a result the ProjectingJackson2HttpMessageConverter matches as a converter with registered halModule and TypeConstrainedMappingJackson2HttpMessageConverter creation is ommited for RequestMappingHandlerAdapter.

One more interesting thing is that there are two more microservices I upgraded. The difference is that one has the same issue and one is working. The one that is working has different spring security and oauth2 setup. Bean of class OAuth2RestTemplate is not defined in the microservice that is working. Microservices with OAuth2RestTemplate have the issue. The reason why I am pointing this out is that there is different in the initialization behavior in these two cases. The OAuth2RestTemplate rest template is populated with these converters too and it might affect the initialization process.

Temporary solution

As a temporary hotfix I have downgraded spring-data-commons from 1.13.6.RELEASE to 1.13.6.RELEASE. However the newer code makes more sense to me.

I am still trying to achieve some better understanding and figure out correct approach

I don't know if it is helpfull to you, but I had a very similar problem with a Spring Boot upgrade from Version 2.0.3 to 2.0.4. I still don't know what exactly caused the problem, but the solution was to create Beans for every Module I use instead of replacing the default ObjectMapper. In your case it would look something like this:

    @Configuration
public class JacksonConfiguration {

    @Bean
    JavaTimeModule javaTimeModule () {
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(ZonedDateTime.class, new ZonedDateTimeSerializer(DateTimeFormatter.ISO_INSTANT));
        return javaTimeModule;
    }
}

All Features can be set via the applications.properties file like this:

spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES=false
spring.jackson.deserialization.READ_DATE_TIMESTAMPS_AS_NANOSECONDS=false

and so on. For more information on how to configure the default object mapper without actually replacing it see https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-customize-the-jackson-objectmapper and https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM