This Tutorial will explain how to produce and consume JSON or XML in Java REST Services with Jersey and MOXy. MOXy is the default JSON-Binding Provider in Jersey 2.x and therefore also in GlassFish 4. However, I have experienced that Jackson is (slightly) faster than MOXy and it is a little easier to configure. For demonstration of how things work we will implement two different REST Services and two simple POJOs as our "model" classes. The REST services will produce and consume JSON and the JSON serialization and de-serialization happens automatically behind the scenes - no need for any extra annotations in the "model" classes. The same thing works also for XML serialization and de-serialization. However, I have disabled that feature to show you that JSON works with simple POJOs and without any additional annotations. XML will work out of the box as soon as you add a the @XmlRootElement annotation to the model classes and enable MediaType.APPLICATION_XML for the corresponding REST services.
You can compile the Maven project and run it on any Servlet Container which supports Servlet API 3.1, i.e. Tomcat 8 or Glassfish 4. Basically, this example even runs on any Servlet API 3.0 compliant Servlet Container, i.e. Tomcat 7 oder Glassfish 3. All you would need to do is changing the web.xml slightly - that's it (see comments).
If you are interested in using Jersey with Jackson then check my other tutorial.
Table of contents:
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.nabisoft.tutorials</groupId>
<artifactId>tomcat-jersey-moxy-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<name>tomcat-jersey-moxy-demo</name>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<version.jdk>1.8</version.jdk> <!-- 1.7 for JDK 7 -->
<version.mvn.compiler>3.2</version.mvn.compiler>
<version.mvn.war.plugin>2.6</version.mvn.war.plugin>
<version.jersey>2.15</version.jersey>
<version.servlet.api>3.1.0</version.servlet.api> <!-- use 3.0.1 for Tomcat 7 or Java EE 6 (i.e. Glassfish 3.x) -->
</properties>
<repositories>
<repository>
<id>java.net-Public</id>
<name>Maven Java Net Snapshots and Releases</name>
<url>https://maven.java.net/content/groups/public/</url>
<layout>default</layout>
</repository>
<repository>
<id>Central</id>
<name>Maven Repository</name>
<url>http://repo1.maven.org/maven2</url>
<layout>default</layout>
</repository>
<repository>
<id>central</id>
<name>Central Repository</name>
<url>http://repo.maven.apache.org/maven2</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
<id>eclipselink</id>
<layout>default</layout>
<name>Repository for library EclipseLink (JPA 2.0)</name>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${version.servlet.api}</version>
<scope>provided</scope>
</dependency>
<!-- Jersey -->
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${version.jersey}</version>
</dependency>
<!-- do not use jettison, prefer jackson
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jettison</artifactId>
<version>${version.jersey}</version>
</dependency>
-->
<!-- do not use Jackson because moxy is jersey default
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${version.jersey}</version>
</dependency>
-->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>${version.jersey}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>${version.jersey}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${version.jersey}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-sse</artifactId>
<version>${version.jersey}</version>
</dependency>
<!-- if you are using Jersey client specific features -->
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>${version.jersey}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${version.mvn.compiler}</version>
<configuration>
<source>${version.jdk}</source>
<target>${version.jdk}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${version.mvn.war.plugin}</version>
<configuration>
<failOnMissingWebXml>true</failOnMissingWebXml>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!-- use this for Servlet API 3 (Tomcat 7, Glassfish 3.x) -->
<!--
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
-->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<name>SESSIONID</name>
</cookie-config>
</session-config>
</web-app>
We will use two very simple POJOs. Make sure to add the @XmlRootElement annotation in case you also want to consume/produce XML. You could also add some JSON annotations (but this is not required).
package com.nabisoft.tutorials.jerseymoxy.model;
import java.util.Date;
//@XmlRootElement //only needed if we also want to generate XML
public class Message {
private String firstName;
private String lastName;
private int age;
private Date date;
private String text;
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
package com.nabisoft.tutorials.jerseymoxy.model;
import java.util.Map;
//@XmlRootElement //only needed if we also want to generate XML
public class Person {
private String firstName;
private String lastName;
private String dateOfBirth;
private String[] citizenships;
private Map<String, Object> creditCards;
private int age;
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 getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(String dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public String[] getCitizenships() {
return citizenships;
}
public void setCitizenships(String[] citizenships) {
this.citizenships = citizenships;
}
public Map<String, Object> getCreditCards() {
return creditCards;
}
public void setCreditCards(Map<String, Object> creditCards) {
this.creditCards = creditCards;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Our two REST Services are very simple as well. Make sure to add MediaType.APPLICATION_XML in case you also want to consume/produce XML.
package com.nabisoft.tutorials.jerseymoxy.jaxrs.resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.nabisoft.tutorials.jerseymoxy.model.Message;
@Path("/message")
public class MessageResource {
@GET
@Path("ping")
public String getServerTime() {
System.out.println("RESTful Service 'MessageService' is running ==> ping");
return "received ping on "+new Date().toString();
}
@GET
@Produces({MediaType.APPLICATION_JSON}) //add MediaType.APPLICATION_XML if you want XML as well (don't forget @XmlRootElement)
public List<Message> getAllMessages() throws Exception{
List<Message> messages = new ArrayList<>();
Message m = new Message();
m.setDate(new Date());
m.setFirstName("Nabi");
//m.setLastName("Zamani");
m.setAge(30);
m.setText("Hello World!");
messages.add(m);
System.out.println("getAllMessages(): found "+messages.size()+" message(s) on DB");
return messages; //do not use Response object because this causes issues when generating XML automatically
}
@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.TEXT_PLAIN})
@Path("/post")
public String postMessage(Message msg) throws Exception{
System.out.println("First Name = "+msg.getFirstName());
System.out.println("Last Name = "+msg.getLastName());
return "ok";
}
}
package com.nabisoft.tutorials.jerseymoxy.jaxrs.resource;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.nabisoft.tutorials.jerseymoxy.model.Person;
@Path("/person")
public class PersonResource {
@Path("get")
@GET
@Produces({MediaType.APPLICATION_JSON}) //add MediaType.APPLICATION_XML if you want XML as well (don't forget @XmlRootElement)
public Person getPerson(){
Person p = new Person();
p.setFirstName("Nabi");
p.setLastName("Zamani");
//p.setDateOfBirth("01.01.2012");
p.setCitizenships( new String[]{"German", "Persian"} );
Map<String, Object> creditCards = new HashMap<String, Object>();
creditCards.put("MasterCard", "1234 1234 1234 1234");
creditCards.put("Visa", "1234 1234 1234 1234");
creditCards.put("dummy", true);
p.setCreditCards(creditCards);
System.out.println("REST call...");
//return Response.ok().entity(p).build();
return p;
}
@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.TEXT_PLAIN})
@Path("/post")
public String postPerson(Person pers) throws Exception{
System.out.println("First Name = "+pers.getFirstName());
System.out.println("Last Name = "+pers.getLastName());
return "ok";
}
}
Now we make sure to use MOXy.
package com.nabisoft.tutorials.jerseymoxy.jaxrs.provider;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.glassfish.jersey.moxy.json.MoxyJsonConfig;
/**
* @see https://jersey.java.net/documentation/latest/user-guide.html#d0e6509
* @author nabizamani
*
*/
@Provider
//@Produces(MediaType.APPLICATION_JSON)
//@Consumes(MediaType.APPLICATION_JSON)
//@Singleton
public class JsonMoxyConfigurationContextResolver implements ContextResolver<MoxyJsonConfig> {
private final MoxyJsonConfig config;
public JsonMoxyConfigurationContextResolver() {
final Map<String, String> namespacePrefixMapper = new HashMap<String, String>();
namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");
config = new MoxyJsonConfig()
.setNamespacePrefixMapper(namespacePrefixMapper)
.setNamespaceSeparator(':')
// .setAttributePrefix("")
// .setValueWrapper("value")
// .property(JAXBContextProperties.JSON_WRAPPER_AS_ARRAY_NAME, true)
.setFormattedOutput(false)
.setIncludeRoot(false)
.setMarshalEmptyCollections(false);
}
@Override
public MoxyJsonConfig getContext(Class<?> objectType) {
return config;
}
}
Now we need to wire everything together by registering the relevant classes.
package com.nabisoft.tutorials.jerseymoxy.jaxrs.application;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/service")
public class ApplicationConfig extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
System.out.println("REST configuration starting: getClasses()");
//features
//this will register MOXy JSON providers
resources.add(org.glassfish.jersey.moxy.json.MoxyJsonFeature.class);
//we could also use this
//resources.add(org.glassfish.jersey.moxy.xml.MoxyXmlFeature.class);
//instead let's do it manually:
resources.add(com.nabisoft.tutorials.jerseymoxy.jaxrs.provider.JsonMoxyConfigurationContextResolver.class);
resources.add(com.nabisoft.tutorials.jerseymoxy.jaxrs.resource.MessageResource.class);
resources.add(com.nabisoft.tutorials.jerseymoxy.jaxrs.resource.PersonResource.class);
//==> we could also choose packages, see below getProperties()
System.out.println("REST configuration ended successfully.");
return resources;
}
@Override
public Set<Object> getSingletons() {
return Collections.emptySet();
}
@Override
public Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<>();
//in Jersey WADL generation is enabled by default, but we don't
//want to expose too much information about our apis.
//therefore we want to disable wadl (http://localhost:8080/service/application.wadl should return http 404)
//see https://jersey.java.net/nonav/documentation/latest/user-guide.html#d0e9020 for details
properties.put("jersey.config.server.wadl.disableWadl", true);
//we could also use something like this instead of adding each of our resources
//explicitely in getClasses():
//properties.put("jersey.config.server.provider.packages", "com.nabisoft.tutorials.mavenstruts.service");
return properties;
}
}
The demo page offers links to directly access the services. Check your console when clicking any of the links or the buttons. The buttons demonstrate how to consume JSON in a REST Service, the links will produce JSON.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Pojo to Json Serialization using Jersey with MOXy for Java REST Services</title>
<script src="<%=request.getContextPath() %>/js/jquery-1.11.2.min.js"></script>
<script>
var ctxPath = "<%=request.getContextPath() %>";
$(function(){
$("#postPerson, #postMessage").on("click", function(){
$.ajax({
url: $(this).attr("id") === "postMessage" ? ctxPath+"/service/message/post" : ctxPath+"/service/person/post",
type: "POST",
data: '{"firstName":"Michael", "lastName":"Jordan"}',
contentType: "application/json",
cache: false,
dataType: "json"
});
});
});
</script>
</head>
<body>
<h1>Pojo to Json Serialization using Jersey with MOXy for Java REST Services</h1>
<ul>
<li><a href="<%=request.getContextPath() %>/service/message"><%=request.getContextPath() %>/service/message</a></li>
<li><a href="<%=request.getContextPath() %>/service/message/ping"><%=request.getContextPath() %>/service/message/ping</a></li>
<li><a href="<%=request.getContextPath() %>/service/person/get"><%=request.getContextPath() %>/service/person/get</a></li>
<li><button id="postPerson">Post Person</button></li>
<li><button id="postMessage">Post Message</button></li>
</ul>
</body>
</html>
Hi First of all thank you for this excellent tutorial which shows how to produce JSON with Java's RESTful Services. This tutorial was definitely worthwhile to read and the sample code was very useful indeed. Actually, this wasn't the first tutorial I have read from Nabisoft.com. Earlier this year I also went through your tutorial on Securing Java EE 6 Web Applications on Glassfish using JAAS which was excellent stuff too. Anyway, in the MessageResource class' getAllMessages method I discovered a small issue. After doing some digging I discovered that you can return a Response after all. I found out from Stackoverflow http://stackoverflow.com/a/20763003/2048391 that if Genson is used, then it'll solve this issue. I just added the following dependency in pom.xml file: <dependency> <groupId>com.owlike</groupId> <artifactId>genson</artifactId> <version>1.3</version> </dependency> After the above change, I got the Response working. Below is a new test method that I used in the MessageResource class: @GET @Path("response") @Produces({MediaType.APPLICATION_JSON}) public Response getAllMessagesResponse() throws Exception{ List<Message> messages = new ArrayList<>(); Message m = new Message(); m.setDate(new Date()); m.setFirstName("Nabi");; m.setAge(30); m.setText("Hello World!"); messages.add(m); return Response.ok(messages).build(); } This Response issue was totally new to me as I am quite new JAVA EE developer - still learning. Currently, I am going through Antonio Goncalve's book Beginning Java EE 7 (http://www.apress.com/9781430246268). From the book I've been learning JAX-RS lately, and I came across with similar Response issue where JSON doesn't work smoothly (maybe a Jersey bug). So I was pleased to find out a workaround. Anyway Many Thanks for these Great Tutorials, Jyrki Mizaras Helsinki Finland