Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Spring Boot not using a @Primary Jackson ObjectMapper for JSON serialization on a rest controller?

I have a class set up to return a customised ObjectMapper. As far as I can find, the correct way to have Spring Boot use this ObjectMapper is to declare it as @Primary, which it is.

@Configuration
public class MyJacksonConfiguration {

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        return Jackson2ObjectMapperBuilder
            .json()
            .findModulesViaServiceLoader(true)
            .mixIn(Throwable.class, ThrowableMixin.class)
            .featuresToDisable(
                    WRITE_DATES_AS_TIMESTAMPS)
            .serializationInclusion(
                    Include.NON_ABSENT)
            .build();
    }
}

However when I return an object from a controller method it is serialized with the default Jackson ObjectMapper configuration.

If I add an explicit ObjectMapper to my controller and call writeValueAsString on it, I can see that this ObjectMapper is the customised one that I would like Spring Boot to use.

@RestController
public class TestController {

    @Autowired
    private TestService service;

    @Autowired
    private ObjectMapper mapper;

    @GetMapping(value = "/test", produces = "application/json")
    public TestResult getResult() {

        final TestResult ret = service.getResult();

        String test = "";
        try {
            test = mapper.writeValueAsString(ret);
            // test now contains the value I'd like returned by the controller!
        } catch (final JsonProcessingException e) {
            e.printStackTrace();
        }

        return ret;
    }
}

When I run tests on my controller the test class also uses an autowired ObjectMapper. Again the ObjectMapper supplied to the test is the customised one.

So Spring knows about the customised ObjectMapper to some extent, but it isn't being used by my rest controller classes.

I have tried turning on Debug logging for Spring but can't see anything useful in logs.

Any idea what might be happening, or where else I should be looking to track down the issue?

EDIT: There appear to be multiple ways to do this, however the way I'm trying to do it appears to be a recommended method and I would like to get it to work this way - see 71.3 of https://docs.spring.io/spring-boot/docs/1.4.7.RELEASE/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper - am I misunderstanding something there?

like image 261
Dan Avatar asked Mar 20 '18 17:03

Dan


People also ask

Does spring boot have Jackson ObjectMapper?

When using JSON format, Spring Boot will use an ObjectMapper instance to serialize responses and deserialize requests. In this article, we will take a look at the most common ways to configure the serialization and deserialization options.

How does spring boot serialize object to JSON?

Spring Boot projects primarily use the JSON library Jackson to serialize and deserialize objects. It is especially useful that Jackson automatically serializes objects returned from REST APIs and deserializes complex type parameters like @RequestBody .

Does spring boot use Jackson?

The Spring Boot parent POM includes Jackson dependencies.

How do I instantiate ObjectMapper?

Simplest usage is of form: final ObjectMapper mapper = new ObjectMapper(); // can use static singleton, inject: just make sure to reuse! MyValue value = new MyValue(); // ... and configure File newState = new File("my-stuff. json"); mapper.


1 Answers

Whilst the other answers show alternative ways of achieving the same result, the actual answer to this question is that I had defined a separate class that extended WebMvcConfigurationSupport. By doing that the WebMvcAutoConfiguration bean had been disabled and so the @Primary ObjectMapper was not picked up by Spring. (Look for @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) in the WebMvcAutoConfiguration source.)

Temporarily removing the class extending WebMvcConfigurationSupport allowed the @Primary ObjectMapper to be picked up and used as expected by Spring.

As I couldn't remove the WebMvcConfigurationSupport extending class permanently, I instead added the following to it:

@Autowired
private ObjectMapper mapper;

@Override
public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) {
    converters.add(new MappingJackson2HttpMessageConverter(mapper));
    addDefaultHttpMessageConverters(converters);
    super.configureMessageConverters(converters);
}
like image 115
Dan Avatar answered Oct 29 '22 17:10

Dan