Search code examples
javaspringspring-mvcjackson

Is There A Better Way To Override The Default MappingJackson2HttpMessageConverter Used By RestClient In Springboot


I need to override the default ObjectMapper dependency (and its default configurations) of MappingJackson2HttpMessageConverter when using the RestClient class of Springboot.

Not every 3rd party API I interact with conforms to the default configuration that Springboot seems to impose.

I was able to successfully override the default ObjectMapper with the following code but:

I'm wondering if there's a better and more obvious way of accomplishing this.

It seems strange to me that changing the default ObjectMapper used by RestClient is so difficult.


    private final ObjectMapper objectMapper;

    public RestClientConfig(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Bean("torii")
    public RestClient toriiApi() {
        return RestClient.builder()
                .messageConverters(this::initMessageConverters)
                .baseUrl(toriiApiHost)
                .defaultHeader("Authorization", generateBasicAuthHeaderValue(torriApiBasicAuthUser, torriApiBasicAuthPassword))
                .defaultHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader("Accepted", MediaType.APPLICATION_JSON_VALUE)
                .build();
    }

    private void initMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        AtomicInteger counter = new AtomicInteger();
        AtomicReference<Integer> jacksonConvertIndex = new AtomicReference<>();
        messageConverters.forEach(httpMessageConverter -> {
            if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter) {
                jacksonConvertIndex.set(counter.get());
            }
            counter.getAndIncrement();
        });
        messageConverters.remove(jacksonConvertIndex.get().intValue());
        messageConverters.add(new MappingJackson2HttpMessageConverter(objectMapper));
    }

Solution

  • You can just set the objectMapper on the existing MappingJackson2HttpMessageConverter as it has a public setter, inherited from AbstractJackson2HttpMessageConverter:

    private void initMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
            messageConverters.forEach(httpMessageConverter -> {
                if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter jacksonConverter) {
                    jacksonConverter.setObjectMapper(objectMapper);
                }
            });
        }
    

    Aside from that tweak, I think this an appropriate use of the API in your situation, which I assume is that you want to retain all the default message converters and only alter the MappingJackson2HttpMessageConverter - the parameter to RestClient.Builder#messageConverters is called configurer so it seems that it is expected to mutate the list. The official documentation also advises you to use the messageConverters method:

    By default, RestClient and RestTemplate register all built-in message converters, depending on the availability of underlying libraries on the classpath. You can also set the message converters to use explicitly, by using the messageConverters() method on the RestClient builder, or via the messageConverters property of RestTemplate.

    And it specifically advises in relation to the MappingJackson2HttpMessageConverter:

    When you need further control (for cases where custom JSON serializers/deserializers need to be provided for specific types), you can inject a custom ObjectMapper through the ObjectMapper property.

    You might prefer if you used the RestClient.builder(RestTemplate) overload and set the messageConverters on the RestTemplate instead, but if you wanted to retain the functionality of using a slightly modified default list, you would end up doing the same things with the messageConverters on the RestTemplate. If you don't care about retaining the rest of the MessageConverters then you can use a RestTemplate with your single desired MappingJackson2HttpMessageConverter.