31

I was unreasonable enough to went into configuring spring beans via annotations and not pure xml beans and now I'm facing the consequences.

I configure REST channels using

<mvc:annotation-driven />

Now I want simply configure the MappingJacksonHttpMessageConverter to output to JSON only this fields that have non-null values. I've tried the following:

<bean id="jsonHttpMessageConverter"
    class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
    <property name="prefixJson" value="false" />
    <property name="supportedMediaTypes" value="application/json" />
    <property name="objectMapper">
        <bean class="org.codehaus.jackson.map.ObjectMapper">
            <property name="serializationInclusion" value="NON_NULL"/>
        </bean>
    </property>
</bean>

The beans gets created, but another instance of converter is created and used in channels. So I've tried the way with @Configuration and @Bean described in this Stackoverflow question, but still json serialization uses its own configuration.

Finally I've tried to inject the mapper via

@Autowired
private MappingJacksonHttpMessageConverter jacksonConverter;

but I've ended with NoSuchBeanDefinitionException. So now I'm out of options and therefore I'm asking for any ideas here. How to controll and configure the mapper used by framework?

Community
  • 1
  • 1
Danubian Sailor
  • 1
  • 38
  • 145
  • 223
  • Try changing the name of the wired field to `jsonHttpMessageConverter` like `@Autowired private MappingJacksonHttpMessageConverter jsonHttpMessageConverter;` – Ahamed Mustafa M May 18 '12 at 09:50

3 Answers3

63

Use the WebMvcConfigurer.configureMessageConverters() method:

Configure the HttpMessageConverters to use [...] If no message converters are added to the list, default converters are added instead.

With @Configuration you have:

@Configuration
class MvcConf extends WebMvcConfigurationSupport {
    protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(converter());
        addDefaultHttpMessageConverters(converters);
    }

    @Bean
    MappingJacksonHttpMessageConverter converter() {
        MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter()
        //do your customizations here...
        return converter;
    }
}

Call to addDefaultHttpMessageConverters() is required because the defaults are not applied when using custom converters.

IMPORTANT NOTE You must remove @EnableWebMvc for your converters to be configured if you extend WebMvcConfigurationSupport.

CupawnTae
  • 14,192
  • 3
  • 29
  • 60
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 2
    still, in my case, only another instance of MappingJacksonHttpMessageConverter is created and it is not the one that is used afterwards – Danubian Sailor May 18 '12 at 11:40
  • 1
    It seems to depend on the order of adding your converter vs default ones. When I add my own converter first like described above it works for me. – user405935 Jan 08 '13 at 21:04
  • Also, don't forget the **versions** must be compatible. I am using Spring 4.* and I tried for hours to set it up with Jackson 1.* - didn't work. Then, I found out, they changed the artifact to com.fasterxml.jackson.core:jackson-databind in Jasckson 2.*. I changed the dependendencies -> worked like a charm (without configureMessageConverters) ;) – pkopac Apr 11 '14 at 08:56
  • 4
    The "IMPORTANT NOTE" saved me! Thanks! – Beto Neto May 30 '14 at 14:27
  • Imprtant note was my life-saver as well... but I'm mad at Spring for this... where in the docs is such a crucial thing clearly spelled out?? – Marko Topolnik Jul 08 '14 at 12:04
  • @MarkoTopolnik - it's spelled out right there: http://docs.spring.io/spring-boot/docs/1.2.0.RELEASE/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration – ETL Dec 16 '14 at 00:14
  • @ETL Irrelevant... that's Spring Boot documentation. – Marko Topolnik Dec 16 '14 at 08:21
  • The important note saved me as well and I was using `@Import(RepositoryRestMvcConfiguration.class)`. Instead of importing my configuration class just needed to extend `RepositoryRestMvcConfiguration` – pllee Apr 02 '15 at 15:50
  • `Cannot resolve method 'add(MappingJacksonHttpMessageConverter)'`. `Cannot resolve method 'addDefaultHttpMessageConverters(List>)'`. `Cannot resolve symbol 'MappingJacksonHttpMessageConverter'`. P.S. Yes, I've added all imports. – Mikhail Batcer Sep 02 '15 at 13:02
  • @DanubianSailor did you ever get around this? – pinkpanther Aug 08 '16 at 18:16
  • 2
    For some unknown reason, this doesn't work for me. Might it be because I have classes extending `WebMvcConfigurationSupport` andother classes extending `WebMvcConfigurerAdapter`? Debugging when app starts up shows in both cases, code is executed by Spring. But still not an objectmapper with my configuration when calling controllers. – Wrench May 18 '17 at 14:28
  • @Wrench comment was the solution for me. I had put a WebMvcConfigurer in my classpath to solve a component scan problem a few weeks ago and now it is messing with my annotated beans. – user327961 Jun 11 '19 at 16:13
  • Hi @Tomasz I tried in my XML configured MVC, but at server start time, I'm getting this: _org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in class path resource [com/acc/ws/rest/config/WebConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set_ – Aakash Patel Sep 20 '21 at 10:15
27

The customization of the spring mvc servlet configuration only in java code can be accomplished in multiple ways.

The simplest one seems to be extending your @Configuration annotated class with WebMvcConfigurerAdapter:

@Configuration
@EnableWebMvc
public class ApplicationSpringConfig extends WebMvcConfigurerAdapter {
    @Override
    public void configureMessageConverters( List<HttpMessageConverter<?>> converters ) {
        converters.add(converter());
    }

    @Bean
    MappingJackson2HttpMessageConverter converter() {
        // [...]
    }
}

Notice that this is lot like the example provided by the answer of Tomasz Nurkiewicz.

However using WebMvcConfigurationSupport instead of WebMvcConfigurerAdapter is more appropriate for Advanced Customizations. That was the case if you needed to also add the default converters.

See the Spring documentation Customizing the Provided Configuration

José Andias
  • 1,894
  • 2
  • 29
  • 31
  • 2
    Why the method `converter()` has to be marked with `@Bean`? – Mikhail Batcer Sep 03 '15 at 07:16
  • 1
    JavaDoc:that adding converters to the list, turns off default converter registration. To simply add a converter without impacting default registration, consider using the method extendMessageConverters(java.util.List) instead. – xedo Oct 04 '15 at 20:14
  • 2
    @MikhailBatcer - The `@Bean` annotation is placed so you can use this method returned object from within your container (spring) context. Also see question for answer contextualization. – José Andias Oct 07 '15 at 14:37
-1

The following solution is for Spring 4.3, (non-boot) where it was necessary to address fetch=FetchType.LAZY by adding a module to the Jackson converters. A similar technique can be used to modify converters in any way, including removal and recreation.

@Configuration
@EnableWebMvc
public class MvcConfig extends WebMvcConfigurerAdapter {

@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {

    for (HttpMessageConverter<?> mc : converters){
         if (mc instanceof MappingJackson2HttpMessageConverter || mc instanceof MappingJackson2XmlHttpMessageConverter) {
            ((AbstractJackson2HttpMessageConverter) mc).getObjectMapper().registerModule(new Hibernate5Module());
        }
    }
    return;
 }

In this case,

  • the WebMvcConfigurerAdapter has a lot of other configuration in it and I wanted to avoid another configuration class.
  • Using extendMessageConverters enabled access to the automatically-configured Jackson classes without losing the configuration of all other message converters, which is what configureMessageConverters would have done.
  • Using registerModuleyou can simply add the needed Hibernate5Module to the existing converters.
  • The module was added to both the JSON and XML processors
sdw
  • 623
  • 9
  • 10