Using JAX RS - beckchr/staxon GitHub Wiki
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.
Let's create a simple web application, which will provide services to produce/consume JSON from/to a JAXB-annotated 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;
}
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();
}
}
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;
}
}
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>
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.
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.