0

I want to obtain the ObjectMapper (or mappers) that Spring 5 creates, configures and uses to serialize and deserialize data exchanges on my Rest resources (i.e. to call readerForUpdating() on it or to provide further configuration such as adding mixins).

I've tried the solutions proposed in this question but none worked: I'm not using Spring Boot and neither of ObjectMapper or MappingJackson2HttpMessageConverter can be @Autowired.

In particular, I've tried reconfiguring the ObjectMapper from the MappingJackson2HttpMessageConverter:

@EnableWebMvc
@Configuration
@EnableSwagger2
@ComponentScan(basePackages=...)
public class WebappConfig implements WebMvcConfigurer {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // ...
        for(HttpMessageConverter<?> c : converters) {

            if(c instanceof MappingJackson2HttpMessageConverter) {
                ObjectMapper o = ((MappingJackson2HttpMessageConverter) c).getObjectMapper();

                //o.configure(SerializationFeature.INDENT_OUTPUT, true);
                o.addMixIn(WorkStamp.class, WorkStampApi.class);
            }
        }
        //...
    }
}

But that's not working either, as that mixin removes a field from the serialized object but the produced JSON still has that field.

watery
  • 5,026
  • 9
  • 52
  • 92

1 Answers1

-1

Here's my solution: basically, during webapp initialization I obtain a reference to the mapper inside the relevant Spring message converter, I then store that reference to be retrieved later as a bean from the context.

This is the Spring webapp configuration class:

@EnableWebMvc
@Configuration
@EnableSwagger2
@ComponentScan(basePackages= { "..." })
@PropertySource("...")
public class WebappConfig implements WebMvcConfigurer {

    private ObjectMapper jacksonMapper;

    @Bean(name="jacksonMapper",autowire=Autowire.BY_NAME)
    public ObjectMapper getMapper() {
        return jacksonMapper;
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        Iterator<HttpMessageConverter<?>> it = converters.iterator();

        while(it.hasNext()) {
            HttpMessageConverter<?> conv = it.next();

            if(conv instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) conv;
                jacksonMapper = jacksonConverter.getObjectMapper();
            }
        }
    }

}

And here's an example of that Object Mapper usage:

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping(path="...")
public class Controller {

    @Autowired
    private ApplicationContext ctx;

    @PostMapping(path= { "..." })
    public ApiResponse<?> post() {

        try {
            // ...

            ObjectMapper om = (ObjectMapper) ctx.getBean("jacksonMapper");

            // ...

            return new ApiResponse<>();
        } catch (Exception e) {
            throw new UnsupportedOperationException(e);
        }
    }

}
watery
  • 5,026
  • 9
  • 52
  • 92
  • (ObjectMapper) ctx.getBean("jacksonMapper") <--- this is what you should never ever do. What's wrong with using "@Autowired ObjectMapper objectMapper" instead? – Agoston Horvath Sep 05 '18 at 13:38
  • @AgostonHorvath (I'm new to Spring) autowiring used to stay null when I wrote this piece of code, so I opted for that - I did a quick try right now but it seems it's being succesfully autowired. – watery Sep 05 '18 at 14:03
  • @AgostonHorvath I found a place where `@Autowiring` doesn't work: with the given `WebappConfig` class code, that bean cannot be injected in a `SmartLifecycle` implementation instance member (although it is only being used in its `start()` method), do you have any suggestion? – watery Sep 12 '18 at 09:32