The title is quite a mouthful but let me explain...For the
DevNexus.com website I want to provide the website's data not only via XML but also via
JSON in order to provide maximum flexibility for other consuming services. E.g.
Pratik Patel pinged me the other day that he intends building an IPad client and
Charlie Collins is planning to update his DevNexus Android client, which he originally created for the 2010 conference (The pressure is on gentleman ;-).
The DevNexus website is based on
Spring MVC 3.0 and provides RESTful Urls that render Html views of the data (Targeting traditional browser clients as well as mobile client using Spring Mobile and jQuery Mobile).
Thus, I configured Restful endpoints as described in
my last blog post. The easiest way to marshal JSON in Spring MVC is via the
Jackson Java JSON-processor. Spring has a few documentation pointers regarding that. Theoretically, the setup is fairly simple and in its simplest forms means to just put the Jackson jar file onto your class path and adding
mvc:annotation-driven to your Spring Xml Context file (See
Chapter 15.12.1 of the Spring documentation). No sweat.
To get started more specifically with Jackson, here are some blog entries that I came accross while diving deeper into it:
While the tutorials may make you think that serializing JSON is happening defacto automatically, it turned out, that I had to fine-tune my domain model for Jackson. My domain model objects have bi-directional relationships and and I was getting a ton of errors of type:
java.lang.StackOverflowError - org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)
You can configure Jackson's marshalling behavior by annotating your domain model objects. However, those annotations are Jackson specific and I already was annotating my obejects with Jaxb annotations for the Xml marshalling of data.
Well - cool beans - , it turns out,
Jackson can indeed use Jaxb annotations to renders Json data (added with version 1.1 of Jackson).
But how do I configure Jackson? How do I make it work in my Spring environment? I came across 2 blogs, that combined, provided the necessary solution.
The first example I found
here, which provides a really nice example.
Furthermore, I not only wanted to configure Jackson to use Jaxb annotations, but I
also needed to make it work for my Spring environment. For that Kyrill Alyoshin has some good information
on his blog (
He also details how to make Jackson work better with Hibernate, which
sounds interesting and I may return to his blog entry more specifically for
that.).
Basically the best way to integrate Jackson and Spring is to
create a custom ObjectMapper class that enables Jaxb support and which can then be injected into Spring's MappingJacksonJsonView.
Here are the things that were needed to get things going for my project -
First, add the following Maven Dependencies (
jackson-xc provides the additional Jaxb support):
<!-- Jackson JSON Mapper -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-lgpl</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-lgpl</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.6.4</version>
</dependency>
The custom ObjectMapper:
import org.codehaus.jackson.map.AnnotationIntrospector;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.xc.JaxbAnnotationIntrospector;
public class JaxbJacksonObjectMapper extends ObjectMapper {
public JaxbJacksonObjectMapper() {
final AnnotationIntrospector introspector
= new JaxbAnnotationIntrospector();
super.getDeserializationConfig()
.setAnnotationIntrospector(introspector);
super.getSerializationConfig()
.setAnnotationIntrospector(introspector);
}
}
The SpringContext configuration that wires everything together:
<bean class="org.springframework.web.servlet.view
.ContentNegotiatingViewResolver">
<property name="order" value="1" />
<property name="ignoreAcceptHeader" value="true" />
<property name="mediaTypes">
<map>
<entry key="xml" value="application/xml"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view
.xml.MarshallingView">
<property name="marshaller"
ref="jaxbMarshaller"/>
</bean>
<bean
class="org.springframework.web
.servlet.view.json.MappingJacksonJsonView">
<property name="objectMapper"
ref="jaxbJacksonObjectMapper"/>
</bean>
</list>
</property>
</bean>