title: In Spring Boot, adding a custom converter by extending MappingJackson2HttpMessageConverter seems to overwrite the existing converter
body: <p>I'm trying to create a converter for a custom media-type like <code>application/vnd.custom.hal+json</code>. I saw this answer <a href="https://stackoverflow.com/questions/12027668/managing-custom-acccept-header-in-spring-mvc">here</a>, but it won't work since you don't have access to the protected constructor of <code>AbstractHttpMessageConverter&lt;T&gt;</code> (super class of <code>MappingJackson2HttpMessageConverter</code>). Which means that the following code does not work:</p>
  
  <pre><code>class MyCustomVndConverter extends MappingJacksonHttpMessageConverter {
      public MyCustomVndConverter (){
          super(MediaType.valueOf("application/vnd.myservice+json"));
      }
  }
  </code></pre>
  
  <p>However, the following does work and basically just mimics what the constructor actually does anyway:</p>
  
  <pre><code>setSupportedMediaTypes(Collections.singletonList(
      MediaType.valueOf("application‌​/vnd.myservice+json")
  ));
  </code></pre>
  
  <p>So I did this for my class, and then added the converter to my existing list of converters by following Spring Boot's documentation <a href="http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-message-converters" rel="nofollow noreferrer">here</a>. My code basically looks like this:</p>
  
  <pre><code>//Defining the converter; the media-type is simply a custom media-type that is 
  //still application/hal+json, i.e., JSON with some additional semantics on top 
  //of what HAL already adds to JSON
  public class TracksMediaTypeConverter extends MappingJackson2HttpMessageConverter {
      public TracksMediaTypeConverter() {
          setSupportedMediaTypes(Collections.singletonList(
              new MediaType("application‌​", "vnd.tracks.v1.hal+json")
          ));
      }
  }
  
  //Adding the message converter
  @Configuration
  @EnableSwagger
  public class MyApplicationConfiguration {
  
      ...    
      @Bean
      public HttpMessageConverters customConverters() {
          return new HttpMessageConverters(new TracksMediaTypeConverter());
      }
  }
  </code></pre>
  
  <p>As per the documentation, this should work. But what I noticed is that this has the effect of <strong>replacing</strong> the existing <code>MappingJackson2HttpMessageCoverter</code>, which handles <code>application/json;charset=UTF-8</code> and <code>application/*+json;charset=UTF-8</code>.</p>
  
  <p>I verified this by attaching a debugger to my app and stepping through breakpoints inside Spring's <code>AbstractMessageCoverterMethodProcessor.java</code> class. There, the private field <code>messageConverters</code> contains the list of converters that have been registered. Normally, i.e., if I do not try to add my converter, I see the following coverters:</p>
  
  <ul>
  <li><code>MappingJackson2HttpMessageCoverter</code> for <code>application/hal+json</code> (I'm assuming this is added by Spring HATEOAS, which I am using)</li>
  <li><code>ByteArrayHttpMessageConverter</code></li>
  <li><code>StringHttpMessageConverter</code></li>
  <li><code>ResourceHttpMessageConverter</code></li>
  <li><code>SourceHttpMessageConverter</code></li>
  <li><code>AllEncompassingFormHttpMessageConverter</code></li>
  <li><code>MappingJackson2HttpMessageConverter</code> for <code>application/json;charset=UTF-8</code> and <code>application/*+json;charset=UTF-8</code></li>
  <li><code>Jaxb2RootElementHttpMessageConverter</code></li>
  </ul>
  
  <p>When I add my custom media type, the second instance of <code>MappingJackson2HttpMessageConverter</code> gets replaced. That is, the list now looks like this:</p>
  
  <ul>
  <li><code>MappingJackson2HttpMessageConverter</code> for <code>application/hal+json</code> (I'm assuming this is added by Spring HATEOAS, which I am using)</li>
  <li><code>ByteArrayHttpMessageConverter</code></li>
  <li><code>StringHttpMessageConverter</code></li>
  <li><code>ResourceHttpMessageConverter</code></li>
  <li><code>SourceHttpMessageConverter</code></li>
  <li><code>AllEncompassingFormHttpMessageConverter</code></li>
  <li><code>MappingJackson2HttpMessageConverter</code> for <code>application/vnd.tracks.v1.hal+json</code> (the existing one has been replaced)</li>
  <li><code>Jaxb2RootElementHttpMessageConverter</code></li>
  </ul>
  
  <p>I'm not entirely sure <em>why</em> this is happening. I stepped through the code and the only thing that really happens is that the no-args constructor of <code>MappingJackson2HttpMessageConverter</code> is called (as it should be), which initially sets the supported media-types to <code>application/json;charset=UTF-8</code> and <code>application/*+json;charset=UTF-8</code>. After that, the list gets overwritten with the media-type that I provide.</p>
  
  <p>What I cannot understand is why adding this media type should <strong>replace</strong> the existing instance of <code>MappingJackson2HttpMessageConverter</code> that handles regular JSON. Is there some strange magic that is going on that does this?</p>
  
  <p>Currently I have a workaround, but I don't like it very much since it's not that elegant and it involves duplication of code already in <code>MappingJackson2HttpMessageConverter</code>.</p>
  
  <p>I created the following class (only changes from the regular <code>MappingJackson2HttpMessageConverter</code> are shown):</p>
  
  <pre><code>public abstract class ExtensibleMappingJackson2HttpMessageConverter&lt;T&gt; extends AbstractHttpMessageConverter&lt;T&gt; implements GenericHttpMessageConverter&lt;T&gt; {
  
      //These constructors are not available in `MappingJackson2HttpMessageConverter`, so
      //I provided them here just for convenience.    
  
      /**
       * Construct an {@code AbstractHttpMessageConverter} with no supported media types.
       * @see #setSupportedMediaTypes
       */
      protected ExtensibleMappingJackson2HttpMessageConverter() {
      }
  
      /**
       * Construct an {@code ExtensibleMappingJackson2HttpMessageConverter} with one supported media type.
       * @param supportedMediaType the supported media type
       */
      protected ExtensibleMappingJackson2HttpMessageConverter(MediaType supportedMediaType) {
          setSupportedMediaTypes(Collections.singletonList(supportedMediaType));
      }
  
      /**
       * Construct an {@code ExtensibleMappingJackson2HttpMessageConverter} with multiple supported media type.
       * @param supportedMediaTypes the supported media types
       */
      protected ExtensibleMappingJackson2HttpMessageConverter(MediaType... supportedMediaTypes) {
          setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));
      }
  
      ...
  
      //These return Object in MappingJackson2HttpMessageConverter because it extends
      //AbstractHttpMessageConverter&lt;Object&gt;. Now these simply return an instance of
      //the generic type. 
  
      @Override
      protected T readInternal(Class&lt;? extends T&gt; clazz, HttpInputMessage inputMessage)
              throws IOException, HttpMessageNotReadableException {
  
          JavaType javaType = getJavaType(clazz, null);
          return readJavaType(javaType, inputMessage);
      }
  
      @Override
      public T read(Type type, Class&lt;?&gt; contextClass, HttpInputMessage inputMessage)
              throws IOException, HttpMessageNotReadableException {
  
          JavaType javaType = getJavaType(type, contextClass);
          return readJavaType(javaType, inputMessage);
      }
  
      private T readJavaType(JavaType javaType, HttpInputMessage inputMessage) {
          try {
              return this.objectMapper.readValue(inputMessage.getBody(), javaType);
          }
          catch (IOException ex) {
              throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex);
          }
      }
  
      ...
  
  }
  </code></pre>
  
  <p>I then use this class as follows:</p>
  
  <pre><code>public class TracksMediaTypeConverter extends ExtensibleMappingJackson2HttpMessageConverter&lt;Tracks&gt; {
      public TracksMediaTypeConverter() {
          super(new MediaType("application", "application/vnd.tracks.v1.hal+json"));
      }
  }
  </code></pre>
  
  <p>The registration of the converter in the configuration class is the same as before. With these changes, the existing instance of <code>MappingJackson2HttpMessageConverter</code> <strong>is not</strong> overwritten and everything works as I would expect.</p>
  
  <p>So to boil everything down, I have two questions:</p>
  
  <ul>
  <li>Why is the existing converter being overwritten when I extend <code>MappingJackson2HttpMessageConverter</code>?</li>
  <li>What is the right way to create a custom media-type converter that represents a semantic media-type that is still basically JSON (and therefore can be serialized and deserialized by <code>MappingJackson2HttpMessageConverter</code>?</li>
  </ul>