Sunday, July 31, 2011

JSon and XML Views on Spring Web MVC

These past weeks I started a research on how was the best way to develop portlets for the liferay content manager, while on that research I came across the Spring Web MVC framework from which I've heard so many good things but until then never developed anything with it.

Even though developing portlets was somehow fun, I learned now why that framework is so loved also for regular web developers. And finally I'm loving it too :)

On this post I'll show you how to render with little efforts views on XML and JSon that are suitable for ajax development. I won't go through the boilerplate configuration for maven dependencies and the basic Spring Web MVC dispatcher portlet, spring context, etc. What I will show is how to configure and inject views suitable for transparently render xml and JSon out of JAX-B annotated Pojos.

If you want to see the whole example project, download it here. It requires Tomcat 7 or another Java EE 6 web container.

Library Requirements:

For the following practice I'll take advantage of the following libraries:

  • The High Performance Jackson JSON processor.
  • Spring OXM Integration (for xml rendering).
If you want to look at the configuration, I've uploaded the sample project to my file application so you cant download it.

Let's get Started.

First of all, We'll take a look at the POJOs we'll be converting to XML and JSon:

 @XmlRootElement  
public class ActionStatus implements Serializable {
private static final long serialVersionUID = 1L;

private boolean success;
private String message;

public ActionStatus() {
}

public ActionStatus(boolean success, String message) {
this.success = success;
this.message = message;
}
... //getters & setters
}

This one is generic and it helps us to provide feedback to the user.

 @XmlRootElement  
public class Contact implements Serializable {
private static final long serialVersionUID = 1L;


private String name;
private String email;
private String phone;

public Contact() {
}

public Contact(String name, String email, String phone) {
this.name = name;
this.email = email;
this.phone = phone;
}
... //getters & setters
}

This one represents a contact which we could push or pull to/from the database.

Please notice that I've only added the @XmlRootElement annotation at class level. This annotation is a JAX-B annotation (you can see a better explanation from this pervious post) and it will help us to convert to both XML and JSon format. (The same way JAX-RS works).

Now we need to configure the views on our applicationContext.xml file (or on the dispatcher servlet xml file if you wish).

XML: MarshallingView

In this case we'll define the view as a bean, this view takes a Marshaller implementation as the constructor argument:

   <!-- the json view -->  
<bean id="jaxbAnnotationBinder" class="org.codehaus.jackson.xc.JaxbAnnotationIntrospector" />
<bean id="jsonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
<bean id="jsonview" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"
p:objectMapper="#{jsonObjectMapper.setAnnotationIntrospector(jaxbAnnotationBinder)}" />

If we want to improve the implementation we can scan the beans from the classpath using the annotation (I'll let it as an exercise).

JSon: MappingJacksonJsonView

In this case we'll define some beans to bootstrap Jackson:
  • A JAX-B annotation introspector: this will use the JAX-B annotations to obtain metadata from the POJO.
  • An ObjectMapper implementation: this will use the given introspector to map our fields to JSON.
Finally, we can define the MappingJacksonJsonView bean as follows. I use a little SpEL to perform the magic trick.

   <!-- the json view -->  
<bean id="jaxbAnnotationBinder" class="org.codehaus.jackson.xc.JaxbAnnotationIntrospector" />
<bean id="jsonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
<bean id="jsonview" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"
p:objectMapper="#{jsonObjectMapper.setAnnotationIntrospector(jaxbAnnotationBinder)}" />

Finally, we can inject the view we wish to use on our controller as follows:

 @Controller  
public class IndexController {

@Autowired
@Qualifier("jsonview")
private View jsonView;

@Autowired
@Qualifier("xmlview")
private View xmlView;

@RequestMapping({"/", "/index.htm"})
public String homePage() {
return "index";
}

@RequestMapping("/contact/blank/xml")
public ModelAndView createBlankContactXml() {
return new ModelAndView(xmlView, "response", new Contact("blank", "blank@blank", "123456789"));
}

@RequestMapping("/contact/blank/json")
public ModelAndView createBlankContactJson() {
return new ModelAndView(jsonView, "response", new Contact("blank", "blank@blank", "123456789"));
}

@RequestMapping("/contact/save")
public ModelAndView createContact(Contact contact) {
System.out.println(contact.toString());
return new ModelAndView(jsonView, "response", new ActionStatus(true, "Woohooo"));
}

}

Note that we could change the way the POJO is rendered by just changing the string on the @Quailifier annotation. I find this approach very easy and elegant.

Hope you find this useful.

3 comments:

  1. Great post Juan!!

    Another way I found really interesting to return JSON/XML responses is using the @ResponseBody annotation that indicates that the object the controller's method returns must be serialized as xml/json (depending on the configuration).

    So.. using your last example, the code would look like this (check the return type of the method, no ModelAndView class at all):

    @RequestMapping("/contact/save")
    @ResponseBody
    public ActionStatus createContact(Contact contact) {
    System.out.println(contact.toString());
    return new ActionStatus(true, "Woohooo");
    }

    ReplyDelete
  2. Indeed, and it has the advantage of the code not depending directly on spring mvc classes.

    Is the content type automatically resolved?

    ReplyDelete
  3. Fantastic post. You now how to enable the WRAP_ROOT_VALUE feature in the applicationContext.xml?

    ReplyDelete