Spring REST Service: how to configure to remove null objects in json response

JavaSpringJackson

Java Problem Overview


I have a spring webservice that returns a json response. I'm using the example given here to create the service: http://www.mkyong.com/spring-mvc/spring-3-mvc-and-json-example/

The format the json is returned in is: {"name":null,"staffName":["kfc-kampar","smith"]}

I want to remove any null objects from the returned response so it looks like this: {"staffName":["kfc-kampar","smith"]}

I've found similar questions asked here but I've been able to get a solution working e.g.

https://stackoverflow.com/questions/7854030/configurating-objectmapper-in-spring

https://stackoverflow.com/questions/10650196/how-to-configure-mmppingjacksonhttpmessageconverter-while-using-spring-annotatio

https://stackoverflow.com/questions/6177913/configuring-the-jacksonobjectmapper-not-working-in-spring-mvc-3

https://stackoverflow.com/questions/6049523/how-to-configure-spring-mvc-3-to-not-return-null-object-in-json-response

https://stackoverflow.com/questions/4823358/spring-configure-responsebody-json-format

https://stackoverflow.com/questions/11392944/jacksonspring3-0-5-custom-object-mapper

From reading through these and other sources, I figured the cleanest way to achieve what I wanted was to use Spring 3.1 and the message-converters that can be configured within the mvc-annotation. My updated spring config file is:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans     
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.1.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

<context:component-scan base-package="com.mkyong.common.controller" />

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
            <property name="prefixJson" value="true" />
		    <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>
    </mvc:message-converters>
</mvc:annotation-driven>

The service class is the same as given on the mkyong.com site, except I commented out the setting of the Shop name variable so it's null i.e.

@Controller
@RequestMapping("/kfc/brands")
public class JSONController {
    @RequestMapping(value="{name}", method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK) 
    public @ResponseBody Shop getShopInJSON(@PathVariable String name) {
	    Shop shop = new Shop();
	    //shop.setName(name);
	    shop.setStaffName(new String[]{name, "cronin"});
	    return shop;
    }
}

The Jackson jars I'm using are jackson-mapper-asl 1.9.0 and jackson-core-asl 1.9.0. These are the only new jars I've added to the pom as provided as part of the spring-json project I downloaded from mkyong.com.

The project builds successfully, but when I invoke the service through the browser I still get the same thing i.e. {"name":null,"staffName":["kfc-kampar","smith"]}

Can anyone tell me where I'm going wrong with my configuration?

I've tried several other options, but the only way I've been able to return the json in the correct format is to add the Object mapper to the JSONController and have the "getShopInJSON" method return a string i.e.

public @ResponseBody String getShopInJSON(@PathVariable String name) throws JsonGenerationException, JsonMappingException, IOException {
	ObjectMapper mapper = new ObjectMapper();
	mapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
	
	Shop shop = new Shop();
	//shop.setName(name);
	shop.setStaffName(new String[]{name, "cronin"});
	String test = mapper.writeValueAsString(shop);
	return test;
}

Now if I invoke the service I get the expected i.e. {"staffName":["kfc-kampar","cronin"]}

I've also been able to get it to work using the @JsonIgnore annotation, but this solution isn't suitable for me.

I don't understand why it's working in code but not in the configuration, so any help would be fantastic.

Java Solutions


Solution 1 - Java

Since Jackson 2.0 you can use JsonInclude

@JsonInclude(Include.NON_NULL)
public class Shop {
    //...
}

Solution 2 - Java

Since Jackson is being used, you have to configure that as a Jackson property. In the case of Spring Boot REST services, you have to configure it in application.properties or application.yml:

spring.jackson.default-property-inclusion = NON_NULL

source

Solution 3 - Java

@JsonSerialize(include=JsonSerialize.Inclusion.NON_EMPTY)
public class Shop {
    //...
}

for jackson 2.0 or later use @JsonInclude(Include.NON_NULL)

This will remove both empty and null Objects.

Solution 4 - Java

Setting the spring.jackson.default-property-inclusion=non_null option is the simplest solution and it works well.

However, be careful if you implement WebMvcConfigurer somewhere in your code, then the property solution will not work and you will have to setup NON_NULL serialization in the code as the following:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    // some of your config here...

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(objectMapper);
        converters.add(jsonConverter);
    }
}

Solution 5 - Java

If you are using Jackson 2, the message-converters tag is:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="prefixJson" value="true"/>
            <property name="supportedMediaTypes" value="application/json"/>
            <property name="objectMapper">
                <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                    <property name="serializationInclusion" value="NON_NULL"/>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

Solution 6 - Java

As of Jackson 2.0, @JsonSerialize(include = xxx) has been deprecated in favour of @JsonInclude

Solution 7 - Java

Since version 1.6 we have new annotation JsonSerialize (in version 1.9.9 for example).

Example:

@JsonSerialize(include=Inclusion.NON_NULL)
public class Test{
...
}

Default value is ALWAYS.

In old versions you can use JsonWriteNullProperties, which is deprecated in new versions. Example:

@JsonWriteNullProperties(false)
public class Test{
    ...
}

Solution 8 - Java

For all you non-xml config folks:

ObjectMapper objMapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
HttpMessageConverter msgConverter = new MappingJackson2HttpMessageConverter(objMapper);
restTemplate.setMessageConverters(Collections.singletonList(msgConverter));

Solution 9 - Java

You can use JsonWriteNullProperties for older versions of Jackson.

For Jackson 1.9+, use JsonSerialize.include.

Solution 10 - Java

I've found a solution through configuring the Spring container, but it's still not exactly what I wanted.

I rolled back to Spring 3.0.5, removed and in it's place I changed my config file to:

	<bean
	class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
	<property name="messageConverters">
		<list>
			<bean
				class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
				<property name="objectMapper" ref="jacksonObjectMapper" />
			</bean>
		</list>
	</property>
</bean>


<bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
<bean id="jacksonSerializationConfig" class="org.codehaus.jackson.map.SerializationConfig"
	factory-bean="jacksonObjectMapper" factory-method="getSerializationConfig" />
<bean
	class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
	<property name="targetObject" ref="jacksonSerializationConfig" />
	<property name="targetMethod" value="setSerializationInclusion" />
	<property name="arguments">
		<list>
			<value type="org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion">NON_NULL</value>
		</list>
	</property>
</bean>

This is of course similar to responses given in other questions e.g.

https://stackoverflow.com/questions/6177913/configuring-the-jacksonobjectmapper-not-working-in-spring-mvc-3

The important thing to note is that mvc:annotation-driven and AnnotationMethodHandlerAdapter cannot be used in the same context.

I'm still unable to get it working with Spring 3.1 and mvc:annotation-driven though. A solution that uses mvc:annotation-driven and all the benefits that accompany it would be far better I think. If anyone could show me how to do this, that would be great.

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
QuestionmikecView Question on Stackoverflow
Solution 1 - JavaWalery StrauchView Answer on Stackoverflow
Solution 2 - JavaLuís SoaresView Answer on Stackoverflow
Solution 3 - JavaJekin KalariyaView Answer on Stackoverflow
Solution 4 - Javamj3cView Answer on Stackoverflow
Solution 5 - JavaEduardoView Answer on Stackoverflow
Solution 6 - JavaA BView Answer on Stackoverflow
Solution 7 - JavagrepView Answer on Stackoverflow
Solution 8 - Javacosbor11View Answer on Stackoverflow
Solution 9 - Javauser647772View Answer on Stackoverflow
Solution 10 - JavamikecView Answer on Stackoverflow