X

Configuring JSON for RESTful Web Services in Jersey 1.0

Guest Author

by Jakub Podlesak

Note: The API for configuring JSON in Jersey has changed with the recently released 1.0.2 version. Please see

http://blogs.sun.com/japod/entry/configuring_json_for_restful_web
.

Jersey 1.0 is an open-source, production-ready reference
implementation of
JAX-RS, the Java API for RESTful Web Services (JSR-311).
Jersey makes it easy to create RESTful web services in Java.

In an earlier Tech Tip,
Implementing
RESTful Web Services in Java
, Paul Sandoz and I introduced RESTful Web Services, JAX-RS, and Jersey, and showed how to
write RESTful web services in Java that conform to the JAX-RS specification.
In this tip you will learn how to configure data in JSON
(JavaScript Object Notation) using Jersey 1.0. JSON is a lightweight data-interchange format that is based on the
object notation of the JavaScript language. Because of it's simple text format, JSON provides a good alternative to other
data interchange formats such as XML and is particularly attractive as a data interchange format for RESTful web services.

In this tip you will build a Jersey-based web application that provides information about printer status. The
application returns the information in JSON format. To build the application, you will use the
Maven 2 software project management tool.
For more information about Maven, see
Welcome to Maven and
Building
Web Applications with Maven 2
.

Creating a Jersey-Based Web Application With Maven 2

The first step in creating a Jersey-based web application with Maven 2 is to create a Maven 2 project. You can do this
by running the following Maven 2 archetype plugin:



   mvn archetype:generate -DarchetypeCatalog=http://download.java.net/maven/2




In response, Maven 2 will prompt you to choose an archetype, that is, a Maven 2 project template, from the archetypes
listed in the archetype catalog, archetype-catalog.xml, at URL http://download.java.net/maven/2:



   Choose archetype:
1: http://download.java.net/maven/2 -> jersey-quickstart-grizzly (Archetype for creating a RESTful web application with Jersey and Grizzly)
2: http://download.java.net/maven/2 -> jersey-quickstart-webapp (Archetype for creating a Jersey based RESTful web application WAR packaging)
Choose a number: (1/2):




Choose 1 (jersey-quickstart-grizzly). You will then be asked to enter a groupid and some other inputs for the
web application. You may use the following values:



   Define value for groupId: : com.example
Define value for artifactId: : printer-status-webapp
Define value for version: 1.0-SNAPSHOT: :
Define value for package: com.example: :
Confirm properties configuration:
groupId: com.example
artifactId: printer-status-webapp
version: 1.0-SNAPSHOT
package: com.example
Y: :




After you confirm the inputs, Maven 2 creates a new subdirectory called printer-status-webapp, which
contains a template for your new Jersey-based web application. Figure 1 shows the expanded file
structure of the printer-status-webapp subdirectory.













Web Application Template Structure Created By Maven 2

Figure 1. Web Application Template Structure Created By Maven 2



Maven 2 also creates a Project Object Model (POM) file, which contains an XML representation of the Maven project.
You can find the pom.xml file in the printer-status-webapp subdirectory.

Adding REST Resources

If you navigate below the printer-status-webapp subdirectory to src/main/java/com/example, you'll
see a Java class named MyResource. This class identifies the URI for a resource that represents a simple message.



   package com.example;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
// The Java class will be hosted at the URI path "/myresource"
@Path("/myresource")
public class MyResource {
// TODO: update the class to suit your needs
// The Java method will process HTTP GET requests
@GET
// The Java method will produce content identified by the MIME Media
// type "text/plain"
@Produces("text/plain")
public String getIt() {
return "Got it!";
}
}




Notice the JAX-RS annotations in the file. The @Path annotation identifies the URI path for which the
MyResource class will serve requests. Recall that an important concept in REST is the existence of resources,
each of which can be referred to using a global identifier, that is, a URI. In order to manipulate these resources,
components of the network, clients and servers, communicate using a standardized interface such as HTTP and exchange
representations of these resources. The @GET annotation indicates that the getIt() method responds
to HTTP GET requests. The @Produces annotation indicates that the getIt() method returns plain text.

To demonstrate JSON functionality in Jersey, let's add some additional REST resources.
In order to do so, you need to enable JSON support in the application.
Edit the pom.xml file, and uncomment the following part:



         <dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>${jersey-version}</version>
</dependency>





You also need to update the jersey-version property in the pom.xml file to the currently
available stable version, 1.0:



    <properties>
<jersey-version>1.0</jersey-version>
</properties>




You're now ready to add JSON resources. To do that, let's create some
Java Architecture for XML Binding (JAXB) beans to model status
information for a printer. Then you'll make the beans accessible as REST resources.

First, in the src/main/java/com/example subdirectory, create a file StatusInfoBean.java with
the following code:



   package com.example;
import java.util.Collection;
import java.util.HashSet;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class StatusInfoBean {
public String status = "Idle";
public int tonerRemaining = 25;
public final Collection<JobInfoBean> jobs = new HashSet<JobInfoBean>();
}




Next, in the src/main/java/com/example subdirectory create a file JobInfoBean.java with the
following code:



   package com.example;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "job")
public class JobInfoBean {
public String name;
public String status;
public int pages;
// just to make JAXB happy
public JobInfoBean(){};
public JobInfoBean(String name, String status, int pages) {
this.name = name;
this.status = status;
this.pages = pages;
}
}




To make the beans accessible as REST resources, you need to update the MyResource.java file in
the src/main/java/com/example subdirectory as follows:



   package com.example;
import com.sun.jersey.spi.resource.Singleton;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Singleton
@Path("/status")
public class MyResource {
StatusInfoBean statusInfoBean = new StatusInfoBean();
{{
statusInfoBean.jobs.add(new JobInfoBean("sample.doc", "printing...", 13));
}}
@GET
@Produces("application/json")
public StatusInfoBean getStatus() {
return statusInfoBean;
}
@PUT
@Consumes("application/json")
public synchronized void setStatus(StatusInfoBean status) {
this.statusInfoBean = status;
}
}




Notice that the getStatus() method returns data in JSON format to the client
and the setStatus() method processes data in JSON format sent by the client:



   @Produces("application/json")
public StatusInfoBean getStatus() {...}
@Consumes("application/json")
public synchronized void setStatus(StatusInfoBean status) {...}




Running the Application

You can run the web application with the following Maven 2 lifecycle command in the
printer-status-webapp subdirectory:



   mvn clean compile exec:java




The command cleans out all Maven 2-generated files and compiles the Java source. It also starts the
Grizzly container with the web application running within it.



   C:\\printer-status-webapp>mvn clean compile exec:java
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'exec'.
[INFO] ------------------------------------------------------------------------
[INFO] Building printer-status-webapp
[INFO] task-segment: [clean, compile, exec:java]
[INFO] ------------------------------------------------------------------------
[INFO] [clean:clean]
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
Downloading: http://download.java.net/maven/2//com/sun/jersey/jersey-server/1.0/
jersey-server-1.0.pom
...
[INFO] [compiler:compile]
[INFO] Compiling 4 source files to C:\\printer-status-webapp\\target\\classes
[INFO] Preparing exec:java
[INFO] No goals needed for project - skipping
[INFO] [exec:java]
Starting grizzly...
Jersey app started with WADL available at http://localhost:9998/application.wadl
Hit enter to stop it...




Open your browser and point it to http://localhost:9998/status. You will see the following:



   {"status":"Idle","tonerRemaining":"25","jobs":{"name":"sample.doc","status":"printing...","pages":"13"}}




This confirms that the Jersey-based web application returns data in JSON format.

Improving the Application

Although the application returns data in JSON format, there are some improvements you might want to make.
For example, if you add another printer job and rerun the application, it would return something like the following:



   {"status":"Idle","tonerRemaining":"25", "jobs":[{"name":"sample.ps","status":"printing...","pages":"13"},{"name":"second.pdf","status":"waiting in queue","pages":"2"}]}




The jobs object in the JSON data is an array, something that wasn't evident in the output for a single printer job.
You might want to change the application so that the result is seen as an array even if it contains just a single element.
Otherwise it could cause some problems when the JSON is processed by the client.

There is another thing you might consider changing. The values for tonerRemaining and pages are
currently listed as strings. You probably want to represent those results as numbers.

To make these improvements, create a file named MyJAXBContextResolver.java in
the src/main/java/com/example subdirectory with the following content:



   package com.example;
import com.sun.jersey.api.json.JSONJAXBContext;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
@Provider
public class MyJAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class[] types = {StatusInfoBean.class, JobInfoBean.class};
public MyJAXBContextResolver() throws Exception {
Map props = new HashMap<String, Object>();
props.put(JSONJAXBContext.JSON_NOTATION, JSONJAXBContext.JSONNotation.MAPPED);
props.put(JSONJAXBContext.JSON_ROOT_UNWRAPPING, Boolean.TRUE);
props.put(JSONJAXBContext.JSON_ARRAYS, new HashSet<String>(1){{add("jobs");}});
props.put(JSONJAXBContext.JSON_NON_STRINGS, new HashSet<String>(1){{add("pages"); add("tonerRemaining");}});
this.context = new JSONJAXBContext(types, props);
}
public JAXBContext getContext(Class<?> objectType) {
return (types[0].equals(objectType)) ? context : null;
}
}




Restart the application with:



   mvn clean compile exec:java




Note that clean is optional here.

You should now see the returned JSON with the results shown as an array -- even for a single print job -- and with the
tonerRemaining and pages results as numbers.



   {"status":"Idle","tonerRemaining":25,"jobs":[{"name":"sample.doc","status":"printing...","pages":13}]}




JSON Configuration Parameters

If you examine the content of the MyJAXBContextResolver class, you'll notice that it includes the following line:



   props.put(JSONJAXBContext.JSON_NOTATION, JSONJAXBContext.JSONNotation.MAPPED);




The option JSONJAXBContext.JSON_NOTATION specifies the type of JSON notation that Jersey 1.0 will generate
from the JAXB beans in the web application. In this case, it specifies that the mapped type of JSON notation will be used.
The mapped notation is the default JSON notation in Jersey 1.0. For this notation, Jersey 1.0 takes a JAXB bean specification
such as this:



   @XmlRootElement
public class StatusInfoBean {
public String status = "Idle";
public int tonerRemaining = 25;
}




And generates the following JSON data:



   {"status":"Idle", "tonerRemaining":"25"}




The following lines in MyJAXBContextResolver specify configuration properties for the default mapped notation:



   props.put(JSONJAXBContext.JSON_ROOT_UNWRAPPING, Boolean.TRUE);
props.put(JSONJAXBContext.JSON_ARRAYS, new HashSet<String>(1){{add("jobs");}});
props.put(JSONJAXBContext.JSON_NON_STRINGS, new HashSet<String>(1){{add("pages"); add("tonerRemaining");}});




In addition to these three properties, a fourth configuration property, JSONJAXBContext.JSON_ATTRS_AS_ELEMS
is available. Table 1 describes the four configuration properties for the mapped notation.




new HashSet<String>(1){{add("pages");}}
Table 1: Configuration Properties for the Mapped Notation
PropertyTypeDescriptionExample
JSONJAXBContext.JSON_ROOT_UNWRAPPINGBooleanIf set to true (default value), root elements corresponding to the XML root element will be stripped out.Boolean.TRUE
JSONJAXBContext.JSON_ARRAYSCollection<String>Contains elements which should be treated as array. Such elements will be delimited with braces.new HashSet<String>(1){{add("jobs");}}
JSONJAXBContext.JSON_NON_STRINGSCollection<String>Contains elements which should not be delimited with double-quotes.
JSONJAXBContext.JSON_ATTRS_AS_ELEMSCollection<String>Contains attributes which should be encoded as elements in JSON (XML attribute names start with @ otherwise).new HashSet<String>(1){{add("attr1");}}

Jersey 1.0 supports two JSON options in addition to JSONJAXBContext.JSON_NOTATION. These are:

  • JSONNotation.MAPPED_JETTISON. This specifies the
    Jettison convention. For the Jettison convention, Jersey 1.0
    generates the following JSON data for the StatusInfoBean:

       {"StatusInfoBean":{"status":"Idle","tonerRemaining":"25"}}





    Jersey currently supports the JSONJAXBContext.JSON_XML2JSON_NS property to configure namespace
    mapping for the Jettison convention.


  • JSONNotation.BADGERFISH. This specifies the
    BadgerFish convention. For the BadgerFish convention, Jersey 1.0
    generates the following JSON data for the StatusInfoBean:

       {"StatusInfoBean":{"status":{"$":"Idle"},"tonerRemaining":{"$":"25"}}}





Further Reading


About the Author

Jakub Podlesak is a member of the Jersey project team. Previously, he participated in the development of Metro,
the GlassFish v2 web services stack, as a member of the WS-Policy team.

Join the discussion

Comments ( 13 )
  • DominoMill Thursday, October 30, 2008

    Thanks for the interesting article. Replacing XML with JSON certainly makes network payload lighter and hence better scalability and response time. But do we have enough parsers out there to read/write JSON? What about environments which doesn't understand JavaScript?


  • Jakub Friday, October 31, 2008

    Hi DominoMill, you can look at the second half of http://www.json.org/ page for a list of languages/libraries supporting JSON. And if you are happy with Jersey, you can use it for processing JSON also on the client side with Jersey client API (http://blogs.sun.com/sandoz/entry/jersey_client_api )


  • Paul Sandoz Friday, October 31, 2008

    @DominoMill: JSON is important because it is easier for JavaScript clients to consume, namely JavaScript engines embedded in browsers making AJAX calls.

    I really cannot say what the performance differences are between producing and consuming JSON and XML. It depends on so many factors. So i would not base my pick on JSON over XML based on performance unless i had empirical data for my particular application indicating that JSON was faster than XML.

    Paul.


  • Dan D Tuesday, November 4, 2008

    There is a typo:

    {"status":"Idle","tonerRemaining":"25", "jobs":[{"name":"sample.ps","status":"printing...","pages":"13"},"name":"second.pdf","status":"waiting in queue","pages":"2"}]}

    There are suppose to be two job objects attached to the jobs property. Looks like a '{' is missing.


  • Edward Ort Tuesday, November 4, 2008

    Thanks for pointing out the typo. It's now fixed.


  • DominoMill Wednesday, November 5, 2008

    @Paul: I didn't mention 'performance' either. XML is more verbose than JSON, hence the message size in bytes is higher. That means it will take longer to push it over network, means more waiting time for user. So the gain is on 'network' side, not on 'CPU'.


  • Jakub Friday, February 13, 2009

    The API for configuring JSON in Jersey has changed with recently released 1.0.2 version. Please see http://blogs.sun.com/japod/entry/configuring_json_for_restful_web for details.


  • Gene Thursday, April 9, 2009

    I have tried everything that I can think of, and when it translates to JSON, it does not use the JSONJAXBContext created. I made my web service class extend PackagesResourceConfig and implement ContextResolver, and I can see that it gets picked up as a @Provider when it deploys, and the methods run fine, but nothing I do changes the JSON output, and print statements for the Context never show. Please tell me what I am missing, as I would really like to get the more "natural" JSON output.


  • Jakub Tuesday, April 14, 2009

    @Gene: Thanks for trying things out. The tip was published almost half a year ago and covers Jersey 1.0. Currently available Jersey archetypes use Jersey version 1.0.2. This might be the reason, why things do not work for you, even if i tried to keep backward compatibility. From your comment, it is hard to say, what concrete thing is wrong. You might want to try the example at http://download.java.net/maven/2/com/sun/jersey/samples/json-from-jaxb/1.0.2/json-from-jaxb-1.0.2-project.zip to get something working out of the box. If you still facing issues, please let me know at users@jersey.dev.java.net mailing list. I would be happy to help you.


  • ravindran Wednesday, July 22, 2009

    Hi,

    I m a newbie.My question can I pass a xml string as a argument to @GET method.I tried using a xml string but it dint let me do it.though it accepts a simple string.Can I use JSON object instead of an XML?Can you suggest some links where I can take a look at the samples.

    Thanks in advance,

    Ravi


  • Jakub Wednesday, July 22, 2009

    @Ravi: You can download a number of Jersey examples as a zip archive at http://download.java.net/maven/2/com/sun/jersey/samples/jersey-samples/1.1.1-ea/jersey-samples-1.1.1-ea-project.zip . JSON could be used instead of XML (see the json-from-jaxb example) but i do not understand, how you use that in the context of GET method, where entity body is not allowed.


  • dizi izle Saturday, January 23, 2010

    thank you for your article...that is a nice one...i often visit this blog and i enjoy reading all posts...


  • Dan Wednesday, October 20, 2010

    Nice article.

    One point i would like to add is, if the bean's field is annotated like

    @XmlElement(name = "Jobs"), then you should use this name while setting the JSONJAXBContext.JSON_ARRAYS property.


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.