Spring HTTP Message Converters Customizing

A deep look at how Spring's HTTP Message Converters work.


A typical REST endpoint implemented using the Spring framework looks as follows:

@RestController
public class SampleController {

  @GetMapping(path = "/data", produces = {
      MediaType.APPLICATION_JSON_VALUE,
      MediaType.APPLICATION_XML_VALUE})
  public SampleData sampleData() {
    return new SampleData(
        new String[]{"sample1", "sample2"});
  }
}

Internally, Spring converts the SampleData object using a HttpMessageConverter.

Spring brings a set of default converters and you can customize your own one. For instance, when the SampleData looks like this:

@Data
@AllArgsConstructor
public class SampleData {

  private String[] data;
}
Then, the default XML conversion ends up with the following structure:
<SampleData>
  <data>
    <data>sample1</data>
    <data>sample2</data>
  </data>
</SampleData>
But you may prefer a different structure:
<SampleData>
  <data>sample1</data>
  <data>sample2</data>
</SampleData>

To achieve this with Spring Boot (you need spring-boot-starter-web dependency and @EnableAutoConfiguration annotation on your application configuration), you have just to register a converter bean:

@Bean
public MappingJackson2XmlHttpMessageConverter 
      mappingJackson2XmlHttpMessageConverter() {
  return new MappingJackson2XmlHttpMessageConverter(
      new Jackson2ObjectMapperBuilder()
          .defaultUseWrapper(false)
          .createXmlMapper(true)
          .build()
  );
}

We could end here as things are so easy with Spring Boot — the converter is registered and used in the needed order automatically.

As a note, it is worth mentioning another option to customize message converters with Spring Boot — a WebMvcConfigurer configuration. The interface comes actually from Spring MVC, but its usage from within a Spring Boot application has some specifics:

@Configuration
class Config implements WebMvcConfigurer {

  @Override
  public void configureMessageConverters(
        List<HttpMessageConverter<?>> converters) {
    converters.add(0, new MappingJackson2XmlHttpMessageConverter(
        new Jackson2ObjectMapperBuilder()
            .defaultUseWrapper(false)
            .createXmlMapper(true)
            .build()
    ));
  }
}

Spring Boot web auto-configuration (concretely WebMvcAutoConfiguration) brings a default configurer which means we can register as many custom configurers we want to without danger to overwrite the default configuration.

That's why we can overwrite the configureMessageConvertersmethod without losing the default message converters, but we have to put it at the beginning of the list to take over from the already included defaults (by converting the first applicable converter will be used).

Spring Boot Aside

If we can't use Spring Boot and must stick with Spring framework only, just registering a converter bean is no more enough.

One option is to use your own WebMvcConfigurer as we already seen above. But the situation without Spring Boot is slightly different.

Spring Web MVC default web configuration (enabled by annotating a configuration with @EnableWebMvc) collects instances of WebMvcConfigurer into a composite where all the converters live in a list.

That means, overwriting the configureMessageConvertersmethod would remove the default converters as well which is, usually, undesirable.

In this case, the method override will do the job. Again, you have to put the custom converter at the beginning of the list:

@EnableWebMvc
@Configuration
class MessageConvertersConfig implements WebMvcConfigurer {

  @Override
  public void extendMessageConverters(
        List<HttpMessageConverter<?>> converters) {
    converters.add(0, new MappingJackson2XmlHttpMessageConverter(
        new Jackson2ObjectMapperBuilder()
            .defaultUseWrapper(false)
            .createXmlMapper(true)
            .build()
    ));
  }
}

Another way is to overwrite the entire configuration. This assumes removing @EnableWebMvc from your codebase. The code is similar to the previous one:

@Configuration
class WebConfig extends WebMvcConfigurationSupport {

  @Override
  protected void extendMessageConverters(
        List<HttpMessageConverter<?>> converters) {
    converters.add(0, new MappingJackson2XmlHttpMessageConverter(
         new Jackson2ObjectMapperBuilder()
            .defaultUseWrapper(false)
            .createXmlMapper(true)
            .build()
    ));
  }
}

As you can see, there is more to be aware of when working without the convenience of using Spring Boot, but it’s still pretty straightforward.

And that’s it. I hope this helped a bit.

You can find working examples on my GitHub.

Happy converting!