Producing and consuming JSON or XML in Java REST Services with Jersey and MOXy

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:


1. Maven configuration (pom.xml)

/pom.xml
<?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>

2. Defining the web.xml

/src/main/webapp/WEB-INF/web.xml
<?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>

3. Implementing POJOs (model classes)

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).

/src/main/java/com/nabisoft/tutorials/jerseymoxy/model/Message.java
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;
    }
    
}

/src/main/java/com/nabisoft/tutorials/jerseymoxy/model/Person.java
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;
    }    
       
}

4. Implementing the REST Services

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.

/src/main/java/com/nabisoft/tutorials/jerseymoxy/jaxrs/resource/MessageResource.java
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";
    }
      
}

/src/main/java/com/nabisoft/tutorials/jerseymoxy/jaxrs/resource/PersonResource.java
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";
    }

}

5. Our MOXy Json Provider

Now we make sure to use MOXy.

/src/main/java/com/nabisoft/tutorials/jerseymoxy/jaxrs/provider/JsonMoxyConfigurationContextResolver.java
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;
    }
}

6. Our JAX-RS Application class

Now we need to wire everything together by registering the relevant classes.


/src/main/java/com/nabisoft/tutorials/jerseymoxy/jaxrs/application/ApplicationConfig.java
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;
    }    
}

7. A simple demo page (index.jsp)

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.

/src/main/webapp/index.jsp
<%@ 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>

8. Download Source Code

Comments
POC Sample
posted by Shebin
Mon Apr 02 17:58:12 UTC 2018
Its good to get this to do POC
java
posted by Radhika V
Fri Nov 10 12:02:17 UTC 2017
Awesome
source
posted by Avani
Thu Aug 17 09:48:37 UTC 2017
I need this for my POC
JAVA
posted by Sarojini
Fri Mar 24 00:26:37 UTC 2017
Need a sample to test
convertion
posted by Satouin Lee
Wed Feb 24 11:14:54 UTC 2016
Very nice tutorial.

Can i get a tutorial on how to convert  the json result to arraylist before displaying it?
for learning
posted by nhan
Tue Feb 16 14:45:44 UTC 2016
merci beaucoup
MessageResource class, getAllMessages method can return Response if Genson used
posted by Jyrki Mizaras
Wed Oct 28 21:05:10 UTC 2015
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