Archive

Archive for the ‘Rest’ Category

JSON/XML/RSS Rest Endpoints using Spring 3.1

May 13th, 2012 1 comment

Spring 3.0 introduced support for REST and this has got even better with Spring 3.1. In this post, I will use Spring 3.1 to create REST endpoints that will serve book information in JSON, XML and RSS formats.

To get started, add the following dependencies to your Spring based web application:

jackson-all-1.9.7.jar for generating JSON output
rome-1.0.jar for generating RSS/Atom feeds
JDOM 1.0 Jar a ROME project dependency

Maven users can get these jars by adding the following dependencies to their pom.xml:

		<dependency>
			<groupId>org.codehaus.jackson</groupId>
			<artifactId>jackson-jaxrs</artifactId>
			<version>1.9.7</version>
		</dependency>
		<dependency>
			<groupId>rome</groupId>
			<artifactId>rome</artifactId>
			<version>1.0</version>
		</dependency>
	

Spring’s REST support is built on top of Spring MVC. So, we start out by creating a Spring MVC web controller as shown below:

package com.inflinx.blog.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.inflinx.blog.domain.Book;

@RequestMapping("/book")
@Controller
public class BookController {
	
	@RequestMapping(method = RequestMethod.GET)
	public String showBook(Model model) 
	{
		model.addAttribute("book", new Book("Practical Spring LDAP", "978-1475265453", "Balaji Varanasi"));
		
		return "book";
	}
	
}

In the above controller, we used the “/book” path with out the usual .html extension. This allows the controller to serve additional media type requests such as JSON and XML. On a GET request,
the showBook method gets invoked. The method’s implementation simply adds the book data to the model.

The Book class implementation is given below. Since we will be using the standard JAXB for creating XML versions of the data, the Book class is annotated with the @XMLRootElement JAXB annotation.

package com.inflinx.blog.domain;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="book")
public class Book 
{
	private String name;
	private String isbn;
	private String author;
	
	public Book()
	{
		
	}
	
	public Book(String name, String isbn, String author)
	{
		this.name = name;
		this.isbn = isbn;
		this.author = author;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getIsbn() {
		return isbn;
	}
	public void setIsbn(String isbn) {
		this.isbn = isbn;
	}
	public String getAuthor() {
		return author;
	}
	public void setAuthor(String author) {
		this.author = author;
	}
}

The “book” view returned by the showCatalog method is implemented using the following book JSP page:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
	<head>
		<title>Home</title>
	</head>
	<body>
		
		${book.name} (${book.isbn}) - ${book.author} 

	</body>
</html>

Finally, the Spring’s web context configuration is shown below:

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

	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<context:component-scan base-package="com.inflinx.blog.web.controller" />
	
</beans:beans>

Up to this point, we have simply created a standard Spring MVC based web application with one controller. When we invoke the controller using the web browser, the following view gets rendered:

Now, converting this controller to serve JSON/XML/RSS variations simply involves configuring a ContentNegotiatingViewResolver. The needed configuration is showing below:


<beans:bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
		<beans:property name="order" value="1" />
		<beans:property name="mediaTypes">
			<beans:map>
				<beans:entry key="json" value="application/json"/>
				<beans:entry key="xml" value="application/xml"/>
				<beans:entry key="rss" value="application/rss+xml"/>
			</beans:map>
		</beans:property>
		<beans:property name="defaultViews">  
    		<beans:list>  
    			<!-- Renders JSON View -->
      			<beans:bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />  
      			
      			<!-- Renders XML View -->
      			<beans:bean class="org.springframework.web.servlet.view.xml.MarshallingView">
					<beans:constructor-arg>
						<beans:bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
							<beans:property name="classesToBeBound">
								<beans:list>
									<beans:value>com.inflinx.blog.domain.Book</beans:value>
								</beans:list>
							</beans:property>
						</beans:bean>
					</beans:constructor-arg>
				</beans:bean>
				
				 <!-- Renders RSS View -->
				<beans:bean class="com.inflinx.blog.web.view.BookRssView" />
    		</beans:list>  
  		</beans:property>		
	</beans:bean>

The ContentNegotiatingViewResolver is a specialized implementation that resolves a view based on the request file extension or Accept header.
In the above configuration we have set the order property to 1 indicating that it must be positioned first in the chain. Using the mediaTypes property, we have provided the mapping from file extension to media types.
Finally, we have used the defaultViews to indicate the classes that needs to be used for creating the view. Upon receiving a request the above view resolver will use the file extension to determine the appropriate view
and delegates the view creation to it.

Out of the box, Spring does not provide an RSS view. So, we need to implement an RSS view specific to the domain object or project. Here is the BookRssView implementation:


package com.inflinx.blog.web.view;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.view.feed.AbstractRssFeedView;

import com.inflinx.blog.domain.Book;
import com.sun.syndication.feed.rss.Channel;
import com.sun.syndication.feed.rss.Item;

public class BookRssView  extends AbstractRssFeedView
{
	@Override
	protected void buildFeedMetadata(Map model, Channel feed,
			HttpServletRequest request) {
		
		feed.setTitle("Book Data");
		feed.setDescription("This is an awesome feed about my book.");
		feed.setLink("blog.inflinx.com");
		
	}
	
	@Override
	protected List buildFeedItems(Map model, HttpServletRequest request, 
				HttpServletResponse response) throws Exception {
		
		// Get the book from the model
		Book book = (Book)model.get("book");
		
		// Create feed items
		List items = new ArrayList();
		Item item = new Item();
		item.setAuthor(book.getAuthor());
		item.setTitle(book.getName());
		item.setLink("http://inflinx.com/ldap");
		items.add(item);
		
		return items;
	}

}


The BookRssView extends Spring’s AbstractRssFeedView. In the buildFeedMetadata we create RSS metadata such as title, link and description. In the buildFeedItems we create the items that needs to be part of the RSS feed.

Now, when we hit the controller with a json extension we will get the JSON output as shown below:

JSON View

Similarly, the XML view is shown below:
XML View

Finally, the RSS view is below:
RSS View

Categories: Rest Tags: , , ,

Using Jackson for Java/JSON Conversion

May 10th, 2012 No comments

Jackson is one of the popular Java libraries that makes dealing with JSON easier. In this post, I will share techniques for converting JSON to Java and vice-versa.

The first thing we need to do is download the Jackson jar.

Maven users can simply include this dependency:

	<dependency>
		<groupId>org.codehaus.jackson</groupId>
  		<artifactId>jackson-jaxrs</artifactId>
  		<version>1.9.7</version>
	</dependency>

Converting Java objects to JSON

Consider a person object with fields first name, last name and ssn. The Java representation of the person is given below:

public class Person {
	
	private String firstName;
	private String lastName;
	private String ssn;
	
	public Person() {
		
	}
	 
	public Person(String firstName, String lastName, String ssn) {
		this.firstName = firstName;
		this.lastName = lastName;
		this.ssn = ssn;
	}
	
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getSsn() {
		return ssn;
	}
	public void setSsn(String ssn) {
		this.ssn = ssn;
	}
	
}

Converting a concrete instance of Person class using Jackson involves creating a new ObjectMapper and invoking the writeValueAsString method. The ObjectMapper instance is threadsafe and can be used across multiple threads. The code is shown below:

	public void convertJavaToJson()
	{
		ObjectMapper objectMapper = new ObjectMapper();
		
		Person person = new Person("John", "Doe", "123-45-6789");
		
		try
		{
			String json = objectMapper.writeValueAsString(person);
			System.out.println(json);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

Upon running the method, the following output is produced:

{“firstName”:”John”,”lastName”:”Doe”,”ssn”:”123-45-6789″}

Jackson automatically converts any nested objects to JSON. For example, lets say each person has an instance of Address class shown below:

public class Address {
	
	private String address1;
	private String city;
	
	public String getAddress1() {
		return address1;
	}
	public void setAddress1(String address1) {
		this.address1 = address1;
	}
	public String getCity() {
		return city;
	}
	public void setCity(String city) {
		this.city = city;
	}
}

The covertJavaToJson method below is converted to add an address and produce JSON:

public void convertJavaToJson()
	{
		ObjectMapper objectMapper = new ObjectMapper();
		
		Person person = new Person("John", "Doe", "123-45-6789");
		
		Address address = new Address();
		address.setAddress1("Main Street");
		address.setCity("Salt Lake City");
		person.setAddress(address);
		
		
		try
		{
			String json = objectMapper.writeValueAsString(person);
			System.out.println(json);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

Here is the resulting JSON:

{“firstName”:”John”,”lastName”:”Doe”,”ssn”:”123-45-6789″,”address”:{“address1″:”Main Street”,”city”:”Salt Lake City”}}

It is possible to customize the JSON produced by Jackson. Let’s say we would like to modify the attribute names in the generated JSON to fName and lName. This can be done by simply annotating the Person’s fields with @@JsonProperty. Here is the modified class:

public class Person {
	
	@JsonProperty("fName")
	private String firstName;
	
	@JsonProperty("lName")
	private String lastName;
	//Other properties and getters/setters	
}

Here is the generated JSON with new attribute names.

{“ssn”:”123-45-6789″,”address”:{“address1″:”Main Street”,”city”:”Salt Lake City”},”fName”:”John”,”lName”:”Doe”}

It is also possible to filter certain fields from being part of the produced JSON. This is done by annotating the field with @JsonIgnore annotation. In the code we below, we placed the annotation on the Address field:

public class Person {
	
	@JsonProperty("fName")
	private String firstName;
	
	@JsonProperty("lName")
	private String lastName;
	private String ssn;
	
	@JsonIgnore
	private Address address;
	
	// Getters and setters
}

Now, when we run the conversion, address will not be included:

{“ssn”:”123-45-6789″,”fName”:”John”,”lName”:”Doe”}

Converting JSON to Java Objects

Let’s say we want to convert the following JSON representation of a person to Person object.

{“firstName”:”Jane”,”lastName”:”Doe”,”ssn”:”456-78-9012″}

In order to perform the conversion, we create an instance of ObjectMapper and invoke the readValue method on it as shown below:

public void convertJSONtoJava()
	{
		String personJson = "{\"firstName\":\"Jane\",\"lastName\":\"Doe\",\"ssn\":\"456-78-9012\"}";
		
		ObjectMapper objectMapper = new ObjectMapper();
		try
		{
			Person person = objectMapper.readValue(personJson, Person.class);
			System.out.println(person);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}

Often times, the Java controllers or Servlets recieve JSON from clients with additional information. For example, the JSON below has an additional action parameter:

{“action”:”modify”,”firstName”:”Jane”,”lastName”:”Doe”,”ssn”:”456-78-9012″}

In those cases, mapping JSON to a Java object will result in UnrecognizedPropertyException:

org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field “action” (Class Person), not marked as ignorable
at [Source: java.io.StringReader@16bdb503; line: 1, column: 12] (through reference chain: Person[“action”])
at org.codehaus.jackson.map.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:53)
at org.codehaus.jackson.map.deser.StdDeserializationContext.unknownFieldException(StdDeserializationContext.java:267)

These situations can be easily addressed by annotating the Person class with @JsonIgnoreProperties annotation. Here is modified class:

@JsonIgnoreProperties(ignoreUnknown=true)
public class Person {
	// Fields and getters/setters
}