How can we configure the internal Jackson mapper when using RestTemplate?

SpringJackson

Spring Problem Overview


I want to update the SerializationConfig.Feature... properties of the jackson mapper used by Spring RestTemplate, Any idea how I can get to it or where I can/should configure it.

Spring Solutions


Solution 1 - Spring

The default RestTemplate constructor registers a set of HttpMessageConverters:

this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter());
this.messageConverters.add(new XmlAwareFormHttpMessageConverter());
if (jaxb2Present) {
	this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jacksonPresent) {
	this.messageConverters.add(new MappingJacksonHttpMessageConverter());
}
if (romePresent) {
	this.messageConverters.add(new AtomFeedHttpMessageConverter());
	this.messageConverters.add(new RssChannelHttpMessageConverter());
}

The MappingJacksonHttpMessageConverter in turns, creates ObjectMapper instance directly. You can either find this converter and replace ObjectMapper or register a new one before it. This should work:

@Bean
public RestOperations restOperations() {
	RestTemplate rest = new RestTemplate();
	//this is crucial!
	rest.getMessageConverters().add(0, mappingJacksonHttpMessageConverter());
	return rest;
}

@Bean
public MappingJacksonHttpMessageConverter mappingJacksonHttpMessageConverter() {
	MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
	converter.setObjectMapper(myObjectMapper());
	return converter;
}

@Bean
public ObjectMapper myObjectMapper() {
	//your custom ObjectMapper here
}

In XML it is something along these lines:

<bean id="restOperations" class="org.springframework.web.client.RestTemplate">
	<property name="messageConverters">
		<util:list>
			<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
			<bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
			<bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/>
			<bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
			<bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter"/>
			<bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/>
			<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
				<property name="objectMapper" ref="customObjectMapper"/>
			</bean>
		</util:list>
	</property>
</bean>

<bean id="customObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>

Note that the transition isn't really 1:1 - I have to explicitly create messageConverters list in XML while with @Configuration approach I could reference existing one and simply modify it. But this should work.

Solution 2 - Spring

If you are not using Spring IOC, you can do something like this (Java 8):

ObjectMapper objectMapper = new ObjectMapper();
// configure your ObjectMapper here

RestTemplate restTemplate = new RestTemplate();    

MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
messageConverter.setPrettyPrint(false);
messageConverter.setObjectMapper(objectMapper);
restTemplate.getMessageConverters().removeIf(m -> m.getClass().getName().equals(MappingJackson2HttpMessageConverter.class.getName()));
restTemplate.getMessageConverters().add(messageConverter);

Solution 3 - Spring

RestTemplate will initialize its default message converters. You should replace the MappingJackson2HttpMessageConverter with your own bean, which should use the object mapper you want to use. This worked for me:

@Bean
public RestTemplate restTemplate() {
	final RestTemplate restTemplate = new RestTemplate();

	//find and replace Jackson message converter with our own
	for (int i = 0; i < restTemplate.getMessageConverters().size(); i++) {
		final HttpMessageConverter<?> httpMessageConverter = restTemplate.getMessageConverters().get(i);
		if (httpMessageConverter instanceof MappingJackson2HttpMessageConverter){
			restTemplate.getMessageConverters().set(i, mappingJackson2HttpMessageConverter());
		}
	}

	return restTemplate;
}

@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
    converter.setObjectMapper(myObjectMapper());
    return converter;
}

@Bean
public ObjectMapper myObjectMapper() {
    // return your own object mapper
}

Solution 4 - Spring

To complete the other answers: if your ObjectMapper just registers a Jackson Module with custom serializers/deserializers, you might want to register your module directly on the existing ObjectMapper from RestTemplate's default MappingJackson2HttpMessageConverter as follows (example without DI but the same applies if using DI):

    SimpleModule module = new SimpleModule();
    module.addSerializer(...);
    module.addDeserializer(...);

    MappingJackson2HttpMessageConverter messageConverter = restTemplate.getMessageConverters().stream()
                    .filter(MappingJackson2HttpMessageConverter.class::isInstance)
                    .map(MappingJackson2HttpMessageConverter.class::cast)
                    .findFirst().orElseThrow( () -> new RuntimeException("MappingJackson2HttpMessageConverter not found"));
    messageConverter.getObjectMapper().registerModule(module);

This will allow you to complete the configuration of the original ObjectMapper (as done by Spring's Jackson2ObjectMapperBuilder), instead of replacing it.

Solution 5 - Spring

Using Spring Boot, it is as simple as:

RestTemplate template = new RestTemplateBuilder()
                            .additionalMessageConverters(new MappingJackson2HttpMessageConverter(objectMapper))
                            .build()

(Tested with Spring Boot 2.2.1)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUsman IsmailView Question on Stackoverflow
Solution 1 - SpringTomasz NurkiewiczView Answer on Stackoverflow
Solution 2 - SpringMatt SidesingerView Answer on Stackoverflow
Solution 3 - SpringmvogiatzisView Answer on Stackoverflow
Solution 4 - SpringOlivier GérardinView Answer on Stackoverflow
Solution 5 - SpringWim DeblauweView Answer on Stackoverflow