Using JAX RS

Christoph Beck edited this page Sep 24, 2012 · 15 revisions

StAXON provides the staxon-jaxrs module, which enables your RESTful services to serialize/deserialize JAXB-annotated classes to/from JSON. It includes the following JAX-RS @Provider classes:

  • de.odysseus.staxon.json.jaxrs.jaxb.JsonXMLObjectProvider is used to read and write JSON objects
  • de.odysseus.staxon.json.jaxrs.jaxb.JsonXMLArrayProvider is used to read and write JSON arrays

To include StAXON's JAX-RS support in your application, you will need to add the staxon and staxon-jaxrs module JARs to your classpath (e.g. put them into your WEB-INF/lib folder in case of a web application). If you use Maven, just add the following dependency to your pom.xml file:

<dependency>
	<groupId>de.odysseus.staxon</groupId>
	<artifactId>staxon-jaxrs</artifactId>
	<version>...</version>
</dependency>

In order to select the StAXON message body readers/writers for your resource, a @JsonXML annotation is required. For a description of the @JsonXML properties, see Using JAXB.

When used with JAX-RS, the @JsonXML annotation can be placed on

  • a model type (@XmlRootElement or @XmlType) to configure its serialization and deserialization
  • a JAX-RS resource method to configure serialization of the result type
  • a parameter of a JAX-RS resource method to configure deserialization of the parameter type

If a @JsonXML annotation is present at a model type and a resource method or parameter, the latter will override the model type annotation. If neither is present, StAXON will not handle the resource.

This is basically it! Let's see how that works in practice.

Example

Let's create a simple web application, which will provide services to produce/consume JSON from/to a JAXB-annotated model.

Model

Our simple model consists of a Customer root entity, with linked Address and PhoneNumber entities.

package de.odysseus.staxon.sample.data;

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import de.odysseus.staxon.json.jaxb.JsonXML;

@JsonXML(
	multiplePaths = { "/phone" }, // trigger JSON array for "phone" list
	virtualRoot = true,           // JSON will omit "customer" root
	prettyPrint = true)           // produce formatted JSON output
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
	@XmlElement(name = "first-name")
	public String firstName;

	@XmlElement(name = "last-name")
	public String lastName;

	@XmlElement
	public Address address;

	@XmlElement(name = "phone")
	public List<PhoneNumber> phoneNumbers;
}
package de.odysseus.staxon.sample.data;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

@XmlAccessorType(XmlAccessType.FIELD)
public class Address {
	@XmlElement
	public String street;
}
package de.odysseus.staxon.sample.data;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlValue;

@XmlAccessorType(XmlAccessType.FIELD)
public class PhoneNumber {
	@XmlValue
	public String number;
}

Resource

The CustomerResource class contains service methods to demonstrate @GET/@POST requests to @Produce/@Consume JSON.

package de.odysseus.staxon.sample.service;

import java.util.ArrayList;

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.Response;

import de.odysseus.staxon.sample.data.Address;
import de.odysseus.staxon.sample.data.Customer;
import de.odysseus.staxon.sample.data.PhoneNumber;

@Path("customer")
public class CustomerResource {
	private Customer newCustomer(
			String firstName, String lastName,
			String street,
			String... phoneNumbers) {
		Customer customer = new Customer();
		customer.firstName = firstName;
		customer.lastName = lastName;
		customer.address = new Address();
		customer.address.street = street;
		customer.phoneNumbers = new ArrayList<PhoneNumber>();
		for (String phoneNumber : phoneNumbers) {
			customer.phoneNumbers.add(new PhoneNumber());
			customer.phoneNumbers.get(customer.phoneNumbers.size() - 1).number = phoneNumber;
		}
		return customer;
	}
	
	@GET
	@Path("get")
	@Produces("application/json")
	public Customer getCustomer() {
		return newCustomer("David", "Lynch", "Mulholland Drive", "555-555-555");
	}

	@GET
	@Path("get/array")
	@Produces("application/json")
	public Customer[] getCustomerArray() {
		return new Customer[]{
			newCustomer("Jack", "London", "Piccadilly Circus"),
			newCustomer("John", "Lennon", "Abbey Road", "123-456-789", "987-654-321") };
	}
	
	@POST
	@Path("post")
	@Consumes("application/json")
	public Response postCustomer(Customer customer) {
		return Response
			.status(201)
			.entity("Received: " + customer.firstName + " " + customer.lastName)
			.build();
	}
	
	@POST
	@Path("post/array")
	@Consumes("application/json")
	public Response postCustomers(Customer[] customers) {
		return Response
			.status(201)
			.entity("Received: " + customers.length + " customers")
			.build();
	}
}

Application

To plug-in the StAXON JAX-RS message body reader/writer classes, you'll need to register them in your javax.ws.rs.core.Application subclass:

package de.odysseus.staxon.sample.service;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

import de.odysseus.staxon.json.jaxrs.jaxb.JsonXMLArrayProvider;
import de.odysseus.staxon.json.jaxrs.jaxb.JsonXMLObjectProvider;

public class CustomerApplication extends Application {
	@Override
	public Set<Class<?>> getClasses() {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		/*
		 * Providers
		 */
		classes.add(JsonXMLObjectProvider.class);
		classes.add(JsonXMLArrayProvider.class);
		/*
		 * Resources
		 */
		classes.add(CustomerResource.class);
		return classes;
	}
}

Web Descriptor

Let's prepare our application to run in a servlet container with Jersey, the JAX-RS reference implementation. The web application descriptor (/WEB-INF/web.xml) looks like this:

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
   <display-name>StAXON JAX-RS Jersey Sample</display-name>

	<servlet>
		<servlet-name>jersey-serlvet</servlet-name>
		<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
		<init-param>
			<param-name>javax.ws.rs.Application</param-name>
			<param-value>de.odysseus.staxon.sample.service.CustomerApplication</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>jersey-serlvet</servlet-name>
		<url-pattern>/restful/*</url-pattern>
	</servlet-mapping>
</web-app>

Build and Deploy

The following Maven pom.xml will build the staxon-jaxrs-sample.war WAR file.

<project xmlns="http://maven.apache.org/POM/4.0.0">
	<modelVersion>4.0.0</modelVersion>

	<groupId>de.odysseus.staxon</groupId>
	<artifactId>staxon-jaxrs-sample</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>StAXON JAX-RS Jersey Sample</name>

	<dependencies>
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-servlet</artifactId>
			<version>1.10</version>
		</dependency>
		<dependency>
			<groupId>de.odysseus.staxon</groupId>
			<artifactId>staxon-jaxrs</artifactId>
			<version>1.0</version>
		</dependency>
	</dependencies>

	<build>
		<finalName>${project.artifactId}</finalName>
	</build>
</project>

To summarize (and please Maven), here's the directory layout for our application:

staxon-jaxrs-sample/
	pom.xml
	src/main/java/
		de/odysseus/staxon/sample/data/
			Address.java
			Customer.java
			PhoneNumber.java
		de/odysseus/staxon/sample/service/
			CustomerApplication.java
			CustomerResource.java
	src/main/webapp/
		WEB-INF/web.xml

Package the application with mvn package and deploy it to a servlet container, e.g. Tomcat or Jetty.

Demo

Assuming your application is running on localhost, port 8080 and with context path staxon-jaxrs-sample, point your browser to

  • http://localhost:8080/staxon-jaxrs-sample/restful/customer/get/ to get

      {
      	"first-name" : "David",
      	"last-name" : "Lynch",
      	"address" : {
      		"street" : "Mulholland Drive"
      	},
      	"phone" : [ "555-555-555" ]
      }
    
  • http://localhost:8080/staxon-jaxrs-sample/restful/customer/get/array/ to get

      [ {
      	"first-name" : "Jack",
      	"last-name" : "London",
      	"address" : {
      		"street" : "Piccadilly Circus"
      	}
      }, {
      	"first-name" : "John",
      	"last-name" : "Lennon",
      	"address" : {
      		"street" : "Abbey Road"
      	},
      	"phone" : [ "123-456-789", "987-654-321" ]
      } ]
    

We'll skip demonstrating the @POST-annotated postCustomer(...) and postCustomers(...) methods, but sending the above JSON output to the URLs for those methods in a HTTP POST request will just be fine.