I have recently upgraded my Spring version to 3.2.0 from 3.1.2. I find that that JSON properties like wrap root element, prevent null values that are defined in ObjectMapper are not working anymore.
Here is the code snippet
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" />
<bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true" />
<property name="ignoreAcceptHeader" value="false" />
<property name="mediaTypes" >
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
and the JSON converter
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="customJacksonObjectMapper"/>
<property name="supportedMediaTypes" value="application/json"/>
</bean>
Object mapper code
public class CustomJacksonObjectMapper extends ObjectMapper {
@SuppressWarnings("deprecation")
public CustomJacksonObjectMapper() {
super();
final AnnotationIntrospector introspector = new JaxbAnnotationIntrospector();
this.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.UNWRAP_ROOT_VALUE, true);
this.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRAP_ROOT_VALUE, true);
this.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRITE_NULL_PROPERTIES, false);
this.setDeserializationConfig(this.getDeserializationConfig().withAnnotationIntrospector(introspector));
this.setSerializationConfig(this.getSerializationConfig().withAnnotationIntrospector(introspector));
}
}
Jackson version
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.7</version>
</dependency>
What could be the issue? Any pointers are appreciated.
The Jackson ObjectMapper class parses a JSON from a string, stream, or file. If Jackson is on the classpath, any controller in Spring applications renders the JSON response by default. To include Jackson on the classpath, we need to add the following dependency in pom.xml:
This article explains how to set content type in Spring MVC, first adding Json mapper in the classpath, then using ResponseEntity, and finally changing the return type from String to Map. As always, all the code snippets can be found over on GitHub.
P.S In Spring 3, to output JSON data, just puts Jackson library in the project classpath. 1. Project Dependencies Get Jackson and Spring dependencies.
1. Overview When using JSON format, Spring Boot will use an ObjectMapper instance to serialize responses and deserialize requests. In this tutorial, we'll take a look at the most common ways to configure the serialization and deserialization options.
Disclaimer: I could not determine why the code in question is not working, but I could reproduce the problem. This answer does provide an alternate approach that works for me.
It could be a bug, as I can reproduce the problem with both with explicit config:
<bean id="jacksonObjectMapper" class="com.demo.CustomJacksonObjectMapper"/>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper"/>
<property name="supportedMediaTypes" value="application/json"/>
</bean>
and via the mvc:message-converter:
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
where both give me {"foo":null,"bar":"bar"}
when using the example class
Demo.java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.codehaus.jackson.annotate.JsonProperty;
@Controller
@RequestMapping("/data")
public class Data {
@RequestMapping
@ResponseBody
public Dummy dataIndex() {
return new Dummy();
}
public class Dummy {
String foo = null;
@JsonProperty
public String foo() {
return foo;
}
String bar = "bar";
@JsonProperty
public String bar() {
return bar;
}
}
}
However, I would have thought the mvc:message-converter method would work, only because I have seen issues overriding the default bean registration that <mvc:annotation-driven/>
performs (see Web MVC Framework) and using the nested configuration is preferred(?).
Maybe the Jackson 2 support has caused some backwards compatibility issues with Jackson 1?
However, switching to the MappingJackson2HttpMessageConverter (supported in Spring 3.1.2 and Spring 3.2), I am able to alter the ObjectMapper configuration to not write null values and wrap the JSON output... but only when using the config inside the <mvc:message-converters/>
!
I get {"Dummy":{"bar":"bar"}}
with the following changes:
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.1.0</version>
</dependency>
CustomJacksonObjectMapper.java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import static com.fasterxml.jackson.annotation.JsonInclude.*;
public class CustomJacksonObjectMapper extends ObjectMapper {
public CustomJacksonObjectMapper() {
super();
this.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
this.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
this.setSerializationInclusion(Include.NON_NULL);
}
}
Demo.java switch to new package structure for Jackson 2
import com.fasterxml.jackson.annotation.JsonProperty;
demo-servlet.xml
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jacksonObjectMapper" />
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
Lastly, according to the SerializationConfig.Feature
documentation, WRITE_NULL_PROPERTIES
feature is deprecated < v2.0 and you should be using SerializationConfig.setSerializationInclusion()
anyway. I assume this is why the @SuppressWarnings("deprecation")
exists in your code. In Jackson >= v2.0, it has been removed, hence the code change in the CustomJacksonObjectMapper.java example.
Configuring ObjectMapper in Spring proposes an alternate solution.
Hope it helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With