Links: Table of Contents | Single HTML

Chapter 9. Support for Common Media Type Representations

9.1. JSON

Jersey JSON support comes as a set of extension modules where each of these modules contains an implementation of a Feature that needs to be registered into your Configurable instance (client/server). There are multiple frameworks that provide support for JSON processing and/or JSON-to-Java binding. The modules listed below provide support for JSON representations by integrating the individual JSON frameworks into Jersey. At present, Jersey integrates with the following modules to provide JSON support:

9.1.1. Approaches to JSON Support

Each of the aforementioned extension modules uses one or more of the three basic approaches available when working with JSON representations:

  • POJO based JSON binding support

  • JAXB based JSON binding support

  • Low-level JSON parsing & processing support

The first method is pretty generic and allows you to map any Java Object to JSON and vice versa. The other two approaches limit you in Java types your resource methods could produce and/or consume. JAXB based approach is useful if you plan to utilize certain JAXB features and support both XML and JSON representations. The last, low-level, approach gives you the best fine-grained control over the out-coming JSON data format.

9.1.1.1. POJO support

POJO support represents the easiest way to convert your Java Objects to JSON and back.

Media modules that support this approach are MOXy and Jackson

9.1.1.2. JAXB based JSON support

Taking this approach will save you a lot of time, if you want to easily produce/consume both JSON and XML data format. With JAXB beans you will be able to use the same Java model to generate JSON as well as XML representations. Another advantage is simplicity of working with such a model and availability of the API in Java SE Platform. JAXB leverages annotated POJOs and these could be handled as simple Java beans.

A disadvantage of JAXB based approach could be if you need to work with a very specific JSON format. Then it might be difficult to find a proper way to get such a format produced and consumed. This is a reason why a lot of configuration options are provided, so that you can control how JAXB beans get serialized and de-serialized. The extra configuration options however requires you to learn more details about the framework you are using.

Following is a very simple example of how a JAXB bean could look like.

Example 9.1. Simple JAXB bean implementation

@XmlRootElement
public class MyJaxbBean {
    public String name;
    public int age;

    public MyJaxbBean() {} // JAXB needs this

    public MyJaxbBean(String name, int age) {
        this.name = name;
        this.age = age;
    }
}


Using the above JAXB bean for producing JSON data format from you resource method, is then as simple as:

Example 9.2. JAXB bean used to generate JSON representation

@GET
@Produces("application/json")
public MyJaxbBean getMyBean() {
    return new MyJaxbBean("Agamemnon", 32);
}


Notice, that JSON specific mime type is specified in @Produces annotation, and the method returns an instance of MyJaxbBean, which JAXB is able to process. Resulting JSON in this case would look like:

{"name":"Agamemnon", "age":"32"}

A proper use of JAXB annotations itself enables you to control output JSON format to certain extent. Specifically, renaming and omitting items is easy to do directly just by using JAXB annotations. For example, the following example depicts changes in the above mentioned MyJaxbBean that will result in {"king":"Agamemnon"} JSON output.

Example 9.3. Tweaking JSON format using JAXB

@XmlRootElement
public class MyJaxbBean {

    @XmlElement(name="king")
    public String name;

    @XmlTransient
    public int age;

    // several lines removed
}


Media modules that support this approach are MOXy, Jackson, Jettison

9.1.1.3. Low-level based JSON support

JSON Processing API is a new standard API for parsing and processing JSON structures in similar way to what SAX and StAX parsers provide for XML. The API is part of Java EE 7 and later. Another such JSON parsing/processing API is provided by Jettison framework. Both APIs provide a low-level access to producing and consuming JSON data structures. By adopting this low-level approach you would be working with JsonObject (or JSONObject respectively) and/or JsonArray (or JSONArray respectively) classes when processing your JSON data representations.

The biggest advantage of these low-level APIs is that you will gain full control over the JSON format produced and consumed. You will also be able to produce and consume very large JSON structures using streaming JSON parser/generator APIs. On the other hand, dealing with your data model objects will probably be a lot more complex, compared to the POJO or JAXB based binding approach. Differences are depicted at the following code snippets.

Let's start with JAXB-based approach.

Example 9.4. JAXB bean creation

MyJaxbBean myBean = new MyJaxbBean("Agamemnon", 32);


Above you construct a simple JAXB bean, which could be written in JSON as {"name":"Agamemnon", "age":32}

Now to build an equivalent JsonObject/JSONObject (in terms of resulting JSON expression), you would need several more lines of code. The following example illustrates how to construct the same JSON data using the standard Java EE 7 JSON-Processing API.

Example 9.5. Constructing a JsonObject (JSON-Processing)

JsonObject myObject = Json.createObjectBuilder()
        .add("name", "Agamemnon")
        .add("age", 32)
        .build();


And at last, here's how the same work can be done with Jettison API.

Example 9.6. Constructing a JSONObject (Jettison)

JSONObject myObject = new JSONObject();
try {
    myObject.put("name", "Agamemnon");
    myObject.put("age", 32);
} catch (JSONException ex) {
    LOGGER.log(Level.SEVERE, "Error ...", ex);
}


Media modules that support the low-level JSON parsing and generating approach are Java API for JSON Processing (JSON-P) and Jettison. Unless you have a strong reason for using the non-standard Jettison API, we recommend you to use the new standard Java API for JSON Processing (JSON-P) API instead.

9.1.2. MOXy

9.1.2.1. Dependency

To use MOXy as your JSON provider you need to add jersey-media-moxy module to your pom.xml file:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-moxy</artifactId>
    <version>2.29.1</version>
</dependency>

If you're not using Maven make sure to have all needed dependencies (see jersey-media-moxy) on the classpath.

9.1.2.2. Configure and register

As stated in the Section 4.3, “Auto-Discoverable Features” as well as earlier in this chapter, MOXy media module is one of the modules where you don't need to explicitly register it's Features (MoxyJsonFeature) in your client/server Configurable as this feature is automatically discovered and registered when you add jersey-media-moxy module to your class-path.

The auto-discoverable jersey-media-moxy module defines a few properties that can be used to control the automatic registration of MoxyJsonFeature (besides the generic CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE an the its client/server variants):

Note

A manual registration of any other Jersey JSON provider feature (except for Java API for JSON Processing (JSON-P)) disables the automated enabling and configuration of MoxyJsonFeature.

To configure MessageBodyReader<T>s / MessageBodyWriter<T>s provided by MOXy you can simply create an instance of MoxyJsonConfig and set values of needed properties. For most common properties you can use a particular method to set the value of the property or you can use more generic methods to set the property:

Example 9.7. MoxyJsonConfig - Setting properties.

final Map<String, String> namespacePrefixMapper = new HashMap<String, String>();
namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");

final MoxyJsonConfig configuration = new MoxyJsonConfig()
        .setNamespacePrefixMapper(namespacePrefixMapper)
        .setNamespaceSeparator(':');
                        


In order to make MoxyJsonConfig visible for MOXy you need to create and register ContextResolver<T> in your client/server code.

Example 9.8. Creating ContextResolver<MoxyJsonConfig>

final Map<String, String> namespacePrefixMapper = new HashMap<String, String>();
namespacePrefixMapper.put("http://www.w3.org/2001/XMLSchema-instance", "xsi");

final MoxyJsonConfig moxyJsonConfig = MoxyJsonConfig()
            .setNamespacePrefixMapper(namespacePrefixMapper)
            .setNamespaceSeparator(':');

final ContextResolver<MoxyJsonConfig> jsonConfigResolver = moxyJsonConfig.resolver();


Another way to pass configuration properties to the underlying MOXyJsonProvider is to set them directly into your Configurable instance (see an example below). These are overwritten by properties set into the MoxyJsonConfig.

Example 9.9. Setting properties for MOXy providers into Configurable

new ResourceConfig()
                            .property(MarshallerProperties.JSON_NAMESPACE_SEPARATOR, ".")
                            // further configuration


There are some properties for which Jersey sets the default value when MessageBodyReader<T> / MessageBodyWriter<T> from MOXy is used and they are:

Table 9.1. Default property values for MOXy MessageBodyReader<T> / MessageBodyWriter<T>

javax.xml.bind.Marshaller#JAXB_FORMATTED_OUTPUTfalse
org.eclipse.persistence.jaxb.JAXBContextProperties#JSON_INCLUDE_ROOT false
org.eclipse.persistence.jaxb.MarshallerProperties#JSON_MARSHAL_EMPTY_COLLECTIONS true
org.eclipse.persistence.jaxb.JAXBContextProperties#JSON_NAMESPACE_SEPARATOR org.eclipse.persistence.oxm.XMLConstants#DOT


Example 9.10. Building client with MOXy JSON feature enabled.

final Client client = ClientBuilder.newBuilder()
        // The line below that registers MOXy feature can be
        // omitted if FEATURE_AUTO_DISCOVERY_DISABLE is
        // not disabled.
        .register(MoxyJsonFeature.class)
        .register(jsonConfigResolver)
        .build();

Example 9.11. Creating JAX-RS application with MOXy JSON feature enabled.

// Create JAX-RS application.
final Application application = new ResourceConfig()
        .packages("org.glassfish.jersey.examples.jsonmoxy")
        // The line below that registers MOXy feature can be
        // omitted if FEATURE_AUTO_DISCOVERY_DISABLE is
        // not disabled.
        .register(MoxyJsonFeature.class)
        .register(jsonConfigResolver);

9.1.2.3. Examples

Jersey provides a JSON MOXy example on how to use MOXy to consume/produce JSON.

9.1.3. Java API for JSON Processing (JSON-P)

9.1.3.1. Dependency

To use JSON-P as your JSON provider you need to add jersey-media-json-processing module to your pom.xml file:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-processing</artifactId>
    <version>2.29.1</version>
</dependency>

If you're not using Maven make sure to have all needed dependencies (see jersey-media-json-processing) on the class-path.

9.1.3.2. Configure and register

As stated in Section 4.3, “Auto-Discoverable Features” JSON-Processing media module is one of the modules where you don't need to explicitly register it's Features (JsonProcessingFeature) in your client/server Configurable as this feature is automatically discovered and registered when you add jersey-media-json-processing module to your classpath.

As for the other modules, jersey-media-json-processing has also few properties that can affect the registration of JsonProcessingFeature (besides CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE and the like):

To configure MessageBodyReader<T>s / MessageBodyWriter<T>s provided by JSON-P you can simply add values for supported properties into the Configuration instance (client/server). Currently supported are these properties:

  • JsonGenerator.PRETTY_PRINTING ("javax.json.stream.JsonGenerator.prettyPrinting")

Example 9.12. Building client with JSON-Processing JSON feature enabled.

ClientBuilder.newClient(new ClientConfig()
        // The line below that registers JSON-Processing feature can be
        // omitted if FEATURE_AUTO_DISCOVERY_DISABLE is not disabled.
        .register(JsonProcessingFeature.class)
        .property(JsonGenerator.PRETTY_PRINTING, true)
);

Example 9.13. Creating JAX-RS application with JSON-Processing JSON feature enabled.

// Create JAX-RS application.
final Application application = new ResourceConfig()
        // The line below that registers JSON-Processing feature can be
        // omitted if FEATURE_AUTO_DISCOVERY_DISABLE is not disabled.
        .register(JsonProcessingFeature.class)
        .packages("org.glassfish.jersey.examples.jsonp")
        .property(JsonGenerator.PRETTY_PRINTING, true);

9.1.3.3. Examples

Jersey provides a JSON Processing example on how to use JSON-Processing to consume/produce JSON.

9.1.4. Jackson (1.x and 2.x)

9.1.4.1. Dependency

To use Jackson 2.x as your JSON provider you need to add jersey-media-json-jackson module to your pom.xml file:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.29.1</version>
</dependency>

To use Jackson 1.x it'll look like:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson1</artifactId>
    <version>2.29.1</version>
</dependency>

If you're not using Maven make sure to have all needed dependencies (see jersey-media-json-jackson or jersey-media-json-jackson1) on the classpath.

9.1.4.2. Configure and register

Note

Note that there is a difference in namespaces between Jackson 1.x (org.codehaus.jackson) and Jackson 2.x (com.fasterxml.jackson).

Jackson JSON processor could be controlled via providing a custom Jackson 2 ObjectMapper (or ObjectMapper for Jackson 1) instance. This could be handy if you need to redefine the default Jackson behaviour and to fine-tune how your JSON data structures look like. Detailed description of all Jackson features is out of scope of this guide. The example below gives you a hint on how to wire your ObjectMapper (ObjectMapper) instance into your Jersey application.

In order to use Jackson as your JSON (JAXB/POJO) provider you need to register JacksonFeature (Jackson1Feature) and a ContextResolver<T> for ObjectMapper, if needed, in your Configurable (client/server).

Example 9.14. ContextResolver<ObjectMapper>

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper> {

    final ObjectMapper defaultObjectMapper;

    public MyObjectMapperProvider() {
        defaultObjectMapper = createDefaultMapper();
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
            return defaultObjectMapper;
        }
    }

    private static ObjectMapper createDefaultMapper() {
        final ObjectMapper result = new ObjectMapper();
        result.configure(Feature.INDENT_OUTPUT, true);

        return result;
    }

    // ...
}

To view the complete example source code, see MyObjectMapperProvider class from the JSON-Jackson example.


Example 9.15. Building client with Jackson JSON feature enabled.

final Client client = ClientBuilder.newBuilder()
        .register(MyObjectMapperProvider.class)  // No need to register this provider if no special configuration is required.
        .register(JacksonFeature.class)
        .build();


Example 9.16. Creating JAX-RS application with Jackson JSON feature enabled.

// Create JAX-RS application.
final Application application = new ResourceConfig()
        .packages("org.glassfish.jersey.examples.jackson")
        .register(MyObjectMapperProvider.class)  // No need to register this provider if no special configuration is required.
        .register(JacksonFeature.class);


9.1.4.3. Examples

Jersey provides JSON Jackson (2.x) example and JSON Jackson (1.x) example showing how to use Jackson to consume/produce JSON.

9.1.5. Jettison

JAXB approach for (de)serializing JSON in Jettison module provides, in addition to using pure JAXB, configuration options that could be set on an JettisonConfig instance. The instance could be then further used to create a JettisonJaxbContext, which serves as a main configuration point in this area. To pass your specialized JettisonJaxbContext to Jersey, you will finally need to implement a JAXBContext ContextResolver<T> (see below).

9.1.5.1. Dependency

To use Jettison as your JSON provider you need to add jersey-media-json-jettison module to your pom.xml file:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jettison</artifactId>
    <version>2.29.1</version>
</dependency>

If you're not using Maven make sure to have all needed dependencies (see jersey-media-json-jettison) on the classpath.

9.1.5.2. JSON Notations

JettisonConfig allows you to use two JSON notations. Each of these notations serializes JSON in a different way. Following is a list of supported notations:

  • JETTISON_MAPPED (default notation)

  • BADGERFISH

You might want to use one of these notations, when working with more complex XML documents. Namely when you deal with multiple XML namespaces in your JAXB beans.

Individual notations and their further configuration options are described below. Rather then explaining rules for mapping XML constructs into JSON, the notations will be described using a simple example. Following are JAXB beans, which will be used.

Example 9.17. JAXB beans for JSON supported notations description, simple address bean

@XmlRootElement
public class Address {
    public String street;
    public String town;

    public Address(){}

    public Address(String street, String town) {
        this.street = street;
        this.town = town;
    }
}


Example 9.18. JAXB beans for JSON supported notations description, contact bean

@XmlRootElement
public class Contact {

    public int id;
    public String name;
    public List<Address> addresses;

    public Contact() {};

    public Contact(int id, String name, List<Address> addresses) {
        this.name = name;
        this.id = id;
        this.addresses =
            (addresses != null) ? new LinkedList<Address>(addresses) : null;
    }
}


Following text will be mainly working with a contact bean initialized with:

Example 9.19. JAXB beans for JSON supported notations description, initialization

Address[] addresses = {new Address("Long Street 1", "Short Village")};
Contact contact = new Contact(2, "Bob", Arrays.asList(addresses));


I.e. contact bean with id=2, name="Bob" containing a single address (street="Long Street 1", town="Short Village").

All below described configuration options are documented also in api-docs at JettisonConfig.

9.1.5.2.1. Jettison mapped notation

If you need to deal with various XML namespaces, you will find Jettison mapped notation pretty useful. Lets define a particular namespace for id item:

...
@XmlElement(namespace="http://example.com")
public int id;
...

Then you simply configure a mapping from XML namespace into JSON prefix as follows:

Example 9.20.  XML namespace to JSON mapping configuration for Jettison based mapped notation

Map<String,String> ns2json = new HashMap<String, String>();
ns2json.put("http://example.com", "example");
context = new JettisonJaxbContext(
    JettisonConfig.mappedJettison().xml2JsonNs(ns2json).build(),
    types);


Resulting JSON will look like in the example below.

Example 9.21. JSON expression with XML namespaces mapped into JSON

{
   "contact":{
      "example.id":2,
      "name":"Bob",
      "addresses":{
         "street":"Long Street 1",
         "town":"Short Village"
      }
   }
}


Please note, that id item became example.id based on the XML namespace mapping. If you have more XML namespaces in your XML, you will need to configure appropriate mapping for all of them.

Another configurable option introduced in Jersey version 2.2 is related to serialization of JSON arrays with Jettison's mapped notation. When serializing elements representing single item lists/arrays, you might want to utilise the following Jersey configuration method to explicitly name which elements to treat as arrays no matter what the actual content is.

Example 9.22.  JSON Array configuration for Jettison based mapped notation

context = new JettisonJaxbContext(
    JettisonConfig.mappedJettison().serializeAsArray("name").build(),
    types);


Resulting JSON will look like in the example below, unimportant lines removed for sanity.

Example 9.23. JSON expression with JSON arrays explicitly configured via Jersey

{
   "contact":{
      ...
      "name":["Bob"],
      ...
   }
}


9.1.5.2.2. Badgerfish notation

From JSON and JavaScript perspective, this notation is definitely the worst readable one. You will probably not want to use it, unless you need to make sure your JAXB beans could be flawlessly written and read back to and from JSON, without bothering with any formatting configuration, namespaces, etc.

JettisonConfig instance using badgerfish notation could be built with

JettisonConfig.badgerFish().build()

and the JSON output JSON will be as follows.

Example 9.24. JSON expression produced using badgerfish notation

{
   "contact":{
      "id":{
         "$":"2"
      },
      "name":{
         "$":"Bob"
      },
      "addresses":{
         "street":{
            "$":"Long Street 1"
         },
         "town":{
            "$":"Short Village"
         }
      }
   }
}


9.1.5.3. Configure and register

In order to use Jettison as your JSON (JAXB/POJO) provider you need to register JettisonFeature and a ContextResolver<T> for JAXBContext (if needed) in your Configurable (client/server).

Example 9.25. ContextResolver<ObjectMapper>

@Provider
public class JaxbContextResolver implements ContextResolver<JAXBContext> {

    private final JAXBContext context;
    private final Set<Class<?>> types;
    private final Class<?>[] cTypes = {Flights.class, FlightType.class, AircraftType.class};

    public JaxbContextResolver() throws Exception {
        this.types = new HashSet<Class<?>>(Arrays.asList(cTypes));
        this.context = new JettisonJaxbContext(JettisonConfig.DEFAULT, cTypes);
    }

    @Override
    public JAXBContext getContext(Class<?> objectType) {
        return (types.contains(objectType)) ? context : null;
    }
}


Example 9.26. Building client with Jettison JSON feature enabled.

final Client client = ClientBuilder.newBuilder()
        .register(JaxbContextResolver.class)  // No need to register this provider if no special configuration is required.
        .register(JettisonFeature.class)
        .build();


Example 9.27. Creating JAX-RS application with Jettison JSON feature enabled.

// Create JAX-RS application.
final Application application = new ResourceConfig()
        .packages("org.glassfish.jersey.examples.jettison")
        .register(JaxbContextResolver.class)  // No need to register this provider if no special configuration is required.
        .register(JettisonFeature.class);


9.1.5.4. Examples

Jersey provides an JSON Jettison example on how to use Jettison to consume/produce JSON.

9.1.6. @JSONP - JSON with Padding Support

Jersey provides out-of-the-box support for JSONP - JSON with padding. The following conditions has to be met to take advantage of this capability:

  • Resource method, which should return wrapped JSON, needs to be annotated with @JSONP annotation.

  • MessageBodyWriter<T> for application/json media type, which also accepts the return type of the resource method, needs to be registered (see JSON section of this chapter).

  • User's request has to contain Accept header with one of the JavaScript media types defined (see below).

Acceptable media types compatible with @JSONP are: application/javascript, application/x-javascript, application/ecmascript, text/javascript, text/x-javascript, text/ecmascript, text/jscript.

Example 9.28. Simplest case of using @JSONP

@GET
@JSONP
@Produces({"application/json", "application/javascript"})
public JaxbBean getSimpleJSONP() {
    return new JaxbBean("jsonp");
}


Assume that we have registered a JSON providers and that the JaxbBean looks like:

Example 9.29. JaxbBean for @JSONP example

@XmlRootElement
public class JaxbBean {

    private String value;

    public JaxbBean() {}

    public JaxbBean(final String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(final String value) {
        this.value = value;
    }
}


When you send a GET request with Accept header set to application/javascript you'll get a result entity that look like:

callback({
    "value" : "jsonp",
})

There are, of course, ways to configure wrapping method of the returned entity which defaults to callback as you can see in the previous example. @JSONP has two parameters that can be configured: callback and queryParam. callback stands for the name of the JavaScript callback function defined by the application. The second parameter, queryParam, defines the name of the query parameter holding the name of the callback function to be used (if present in the request). Value of queryParam defaults to __callback so even if you do not set the name of the query parameter yourself, client can always affect the result name of the wrapping JavaScript callback method.

Note

queryParam value (if set) always takes precedence over callback value.

Lets modify our example a little bit:

Example 9.30. Example of @JSONP with configured parameters.

@GET
@Produces({"application/json", "application/javascript"})
@JSONP(callback = "eval", queryParam = "jsonpCallback")
public JaxbBean getSimpleJSONP() {
    return new JaxbBean("jsonp");
}


And make two requests:

curl -X GET http://localhost:8080/jsonp

will return

eval({
    "value" : "jsonp",
})

and the

curl -X GET http://localhost:8080/jsonp?jsonpCallback=alert

will return

alert({
    "value" : "jsonp",
})

Example.  You can take a look at a provided JSON with Padding example.

9.2. XML

As you probably already know, Jersey uses MessageBodyWriter<T>s and MessageBodyReader<T>s to parse incoming requests and create outgoing responses. Every user can create its own representation but... this is not recommended way how to do things. XML is proven standard for interchanging information, especially in web services. Jerseys supports low level data types used for direct manipulation and JAXB XML entities.

9.2.1. Low level XML support

Jersey currently support several low level data types: StreamSource, SAXSource, DOMSource and Document. You can use these types as the return type or as a method (resource) parameter. Lets say we want to test this feature and we have helloworld example as a starting point. All we need to do is add methods (resources) which consumes and produces XML and types mentioned above will be used.

Example 9.31. Low level XML test - methods added to HelloWorldResource.java

@POST
@Path("StreamSource")
public StreamSource getStreamSource(StreamSource streamSource) {
    return streamSource;
}

@POST
@Path("SAXSource")
public SAXSource getSAXSource(SAXSource saxSource) {
    return saxSource;
}

@POST
@Path("DOMSource")
public DOMSource getDOMSource(DOMSource domSource) {
    return domSource;
}

@POST
@Path("Document")
public Document getDocument(Document document) {
    return document;
}

Both MessageBodyWriter<T> and MessageBodyReader<T> are used in this case, all we need is a POST request with some XML document as a request entity. To keep this as simple as possible only root element with no content will be sent: "<test />". You can create JAX-RS client to do that or use some other tool, for example curl:

curl -v http://localhost:8080/base/helloworld/StreamSource -d "<test/>"

You should get exactly the same XML from our service as is present in the request; in this case, XML headers are added to response but content stays. Feel free to iterate through all resources.

9.2.2. Getting started with JAXB

Good start for people which already have some experience with JAXB annotations is JAXB example. You can see various use-cases there. This text is mainly meant for those who don't have prior experience with JAXB. Don't expect that all possible annotations and their combinations will be covered in this chapter, JAXB (JSR 222 implementation) is pretty complex and comprehensive. But if you just want to know how you can interchange XML messages with your REST service, you are looking at the right chapter.

Lets start with simple example. Lets say we have class Planet and service which produces "Planets".

Example 9.32. Planet class

@XmlRootElement
public class Planet {
    public int id;
    public String name;
    public double radius;
}

Example 9.33. Resource class

@Path("planet")
public class Resource {

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public Planet getPlanet() {
        final Planet planet = new Planet();

        planet.id = 1;
        planet.name = "Earth";
        planet.radius = 1.0;

        return planet;
    }
}

You can see there is some extra annotation declared on Planet class, particularly @XmlRootElement. This is an JAXB annotation which maps java classes to XML elements. We don't need to specify anything else, because Planet is very simple class and all fields are public. In this case, XML element name will be derived from the class name or you can set the name property: @XmlRootElement(name="yourName").

Our resource class will respond to GET /planet with

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<planet>
    <id>1</id>
    <name>Earth</name>
    <radius>1.0</radius>
</planet>

which might be exactly what we want... or not. Or we might not really care, because we can use JAX-RS client for making requests to this resource and this is easy as:

                    Planet planet = webTarget.path("planet").request(MediaType.APPLICATION_XML_TYPE).get(Planet.class);
                

There is pre-created WebTarget object which points to our applications context root and we simply add path (in our case its planet), accept header (not mandatory, but service could provide different content based on this header; for example text/html can be served for web browsers) and at the end we specify that we are expecting Planet class via GET request.

There may be need for not just producing XML, we might want to consume it as well.

Example 9.34. Method for consuming Planet

@POST
@Consumes(MediaType.APPLICATION_XML)
public void setPlanet(Planet planet) {
    System.out.println("setPlanet " + planet);
}


After valid request is made, service will print out string representation of Planet, which can look like Planet{id=2, name='Mars', radius=1.51}. With JAX-RS client you can do:

                    webTarget.path("planet").request().post(Entity.xml(planet));
                

If there is a need for some other (non default) XML representation, other JAXB annotations would need to be used. This process is usually simplified by generating java source from XML Schema which is done by xjc which is XML to java compiler and it is part of JAXB.

9.2.3. POJOs

Sometimes you can't / don't want to add JAXB annotations to source code and you still want to have resources consuming and producing XML representation of your classes. In this case, JAXBElement class should help you. Let's redo planet resource but this time we won't have an @XmlRootElement annotation on Planet class.

Example 9.35. Resource class - JAXBElement

@Path("planet")
public class Resource {

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public JAXBElement<Planet> getPlanet() {
        Planet planet = new Planet();

        planet.id = 1;
        planet.name = "Earth";
        planet.radius = 1.0;

        return new JAXBElement<Planet>(new QName("planet"), Planet.class, planet);
    }

    @POST
    @Consumes(MediaType.APPLICATION_XML)
    public void setPlanet(JAXBElement<Planet> planet) {
        System.out.println("setPlanet " + planet.getValue());
    }
}

As you can see, everything is little more complicated with JAXBElement. This is because now you need to explicitly set element name for Planet class XML representation. Client side is even more complicated than server side because you can't do JAXBElement<Planet> so JAX-RS client API provides way how to workaround it by declaring subclass of GenericType<T>.

Example 9.36. Client side - JAXBElement

// GET
GenericType<JAXBElement<Planet>> planetType = new GenericType<JAXBElement<Planet>>() {};

Planet planet = (Planet) webTarget.path("planet").request(MediaType.APPLICATION_XML_TYPE).get(planetType).getValue();
System.out.println("### " + planet);

// POST
planet = new Planet();

// ...

webTarget.path("planet").post(new JAXBElement<Planet>(new QName("planet"), Planet.class, planet));

9.2.4. Using custom JAXBContext

In some scenarios you can take advantage of using custom JAXBContext. Creating JAXBContext is an expensive operation and if you already have one created, same instance can be used by Jersey. Other possible use-case for this is when you need to set some specific things to JAXBContext, for example to set a different class loader.

Example 9.37. PlanetJAXBContextProvider

@Provider
public class PlanetJAXBContextProvider implements ContextResolver<JAXBContext> {
    private JAXBContext context = null;

    public JAXBContext getContext(Class<?> type) {
        if (type != Planet.class) {
            return null; // we don't support nothing else than Planet
        }

        if (context == null) {
            try {
                context = JAXBContext.newInstance(Planet.class);
            } catch (JAXBException e) {
                // log warning/error; null will be returned which indicates that this
                // provider won't/can't be used.
            }
        }

        return context;
    }
}

Sample above shows simple JAXBContext creation, all you need to do is put this @Provider annotated class somewhere where Jersey can find it. Users sometimes have problems with using provider classes on client side, so just to reminder - you have to declare them in the client config (client does not do anything like package scanning done by server).

Example 9.38. Using Provider with JAX-RS client

ClientConfig config = new ClientConfig();
config.register(PlanetJAXBContextProvider.class);

Client client = ClientBuilder.newClient(config);
                

9.2.5. MOXy

If you want to use MOXy as your JAXB implementation instead of JAXB RI you have two options. You can either use the standard JAXB mechanisms to define the JAXBContextFactory from which a JAXBContext instance would be obtained (for more on this topic, read JavaDoc on JAXBContext) or you can add jersey-media-moxy module to your project and register/configure MoxyXmlFeature class/instance in the Configurable.

Example 9.39. Add jersey-media-moxy dependency.

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-moxy</artifactId>
    <version>2.29.1</version>
</dependency>

Example 9.40. Register the MoxyXmlFeature class.

final ResourceConfig config = new ResourceConfig()
    .packages("org.glassfish.jersey.examples.xmlmoxy")
    .register(MoxyXmlFeature.class);

Example 9.41. Configure and register an MoxyXmlFeature instance.

// Configure Properties.
final Map<String, Object> properties = new HashMap<String, Object>();
// ...

// Obtain a ClassLoader you want to use.
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

final ResourceConfig config = new ResourceConfig()
    .packages("org.glassfish.jersey.examples.xmlmoxy")
    .register(new MoxyXmlFeature(
        properties,
        classLoader,
        true, // Flag to determine whether eclipselink-oxm.xml file should be used for lookup.
        CustomClassA.class, CustomClassB.class  // Classes to be bound.
    ));

9.3. Multipart

9.3.1. Overview

The classes in this module provide an integration of multipart/* request and response bodies in a JAX-RS runtime environment. The set of registered providers is leveraged, in that the content type for a body part of such a message reuses the same MessageBodyReader<T>/MessageBodyWriter<T> implementations as would be used for that content type as a standalone entity.

The following list of general MIME MultiPart features is currently supported:

  • The MIME-Version: 1.0 HTTP header is included on generated responses. It is accepted, but not required, on processed requests.

  • A MessageBodyReader<T> implementation for consuming MIME MultiPart entities.

  • A MessageBodyWriter<T> implementation for producing MIME MultiPart entities. The appropriate @Provider is used to serialize each body part, based on its media type.

  • Optional creation of an appropriate boundary parameter on a generated Content-Type header, if not already present.

For more information refer to Multi Part.

9.3.1.1. Dependency

To use multipart features you need to add jersey-media-multipart module to your pom.xml file:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-multipart</artifactId>
    <version>2.29.1</version>
</dependency>

If you're not using Maven make sure to have all needed dependencies (see jersey-media-multipart) on the class-path.

9.3.1.2. Registration

Before you can use capabilities of the jersey-media-multipart module in your client/server code, you need to register MultiPartFeature.

Example 9.42. Building client with MultiPart feature enabled.

final Client client = ClientBuilder.newBuilder()
    .register(MultiPartFeature.class)
    .build();


Example 9.43. Creating JAX-RS application with MultiPart feature enabled.

// Create JAX-RS application.
final Application application = new ResourceConfig()
    .packages("org.glassfish.jersey.examples.multipart")
    .register(MultiPartFeature.class)


9.3.1.3. Examples

Jersey provides a Multipart Web Application Example on how to use multipart features.

9.3.2. Client

MultiPart class (or it's subclasses) can be used as an entry point to using jersey-media-multipart module on the client side. This class represents a MIME multipart message and is able to hold an arbitrary number of BodyParts. Default media type is multipart/mixed for MultiPart entity and text/plain for BodyPart.

Example 9.44. MultiPart entity

final MultiPart multiPartEntity = new MultiPart()
        .bodyPart(new BodyPart().entity("hello"))
        .bodyPart(new BodyPart(new JaxbBean("xml"), MediaType.APPLICATION_XML_TYPE))
        .bodyPart(new BodyPart(new JaxbBean("json"), MediaType.APPLICATION_JSON_TYPE));

final WebTarget target = // Create WebTarget.
final Response response = target
        .request()
        .post(Entity.entity(multiPartEntity, multiPartEntity.getMediaType()));


If you send a multiPartEntity to the server the entity with Content-Type header in HTTP message would look like (don't forget to register a JSON provider):

Example 9.45. MultiPart entity in HTTP message.

Content-Type: multipart/mixed; boundary=Boundary_1_829077776_1369128119878

--Boundary_1_829077776_1369128119878
Content-Type: text/plain

hello
--Boundary_1_829077776_1369128119878
Content-Type: application/xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><jaxbBean><value>xml</value></jaxbBean>
--Boundary_1_829077776_1369128119878
Content-Type: application/json

{"value":"json"}
--Boundary_1_829077776_1369128119878--


When working with forms (e.g. media type multipart/form-data) and various fields in them, there is a more convenient class to be used - FormDataMultiPart. It automatically sets the media type for the FormDataMultiPart entity to multipart/form-data and Content-Disposition header to FormDataBodyPart body parts.

Example 9.46. FormDataMultiPart entity

final FormDataMultiPart multipart = new FormDataMultiPart()
    .field("hello", "hello")
    .field("xml", new JaxbBean("xml"))
    .field("json", new JaxbBean("json"), MediaType.APPLICATION_JSON_TYPE);

final WebTarget target = // Create WebTarget.
final Response response = target.request().post(Entity.entity(multipart, multipart.getMediaType()));


To illustrate the difference when using FormDataMultiPart instead of FormDataBodyPart you can take a look at the FormDataMultiPart entity from HTML message:

Example 9.47. FormDataMultiPart entity in HTTP message.

Content-Type: multipart/form-data; boundary=Boundary_1_511262261_1369143433608

--Boundary_1_511262261_1369143433608
Content-Type: text/plain
Content-Disposition: form-data; name="hello"

hello
--Boundary_1_511262261_1369143433608
Content-Type: application/xml
Content-Disposition: form-data; name="xml"

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><jaxbBean><value>xml</value></jaxbBean>
--Boundary_1_511262261_1369143433608
Content-Type: application/json
Content-Disposition: form-data; name="json"

{"value":"json"}
--Boundary_1_511262261_1369143433608--


A common use-case for many users is sending files from client to server. For this purpose you can use classes from org.glassfish.jersey.jersey.media.multipart package, such as FileDataBodyPart or StreamDataBodyPart.

Example 9.48. Multipart - sending files.

// MediaType of the body part will be derived from the file.
final FileDataBodyPart filePart = new FileDataBodyPart("my_pom", new File("pom.xml"));

final FormDataMultiPart multipart = new FormDataMultiPart()
    .field("foo", "bar")
    .bodyPart(filePart);

final WebTarget target = // Create WebTarget.
final Response response = target.request()
    .post(Entity.entity(multipart, multipart.getMediaType()));


Warning

Do not use ApacheConnectorProvider nor GrizzlyConnectorProvider neither JettyConnectorProvider connector implementations with Jersey Multipart features. See Header modification issue warning for more details.

9.3.3. Server

Returning a multipart response from server to client is not much different from the parts described in the client section above. To obtain a multipart entity, sent by a client, in the application you can use two approaches:

  • Injecting the whole MultiPart entity.

  • Injecting particular parts of a form-data multipart request via @FormDataParam annotation.

9.3.3.1. Injecting and returning the MultiPart entity

Working with MultiPart types is no different from injecting/returning other entity types. Jersey provides MessageBodyReader<T> for reading the request entity and injecting this entity into a method parameter of a resource method and MessageBodyWriter<T> for writing output entities. You can expect that either MultiPart or FormDataMultiPart (multipart/form-data media type) object to be injected into a resource method.

Example 9.49. Resource method using MultiPart as input parameter / return value.

@POST
@Produces("multipart/mixed")
public MultiPart post(final FormDataMultiPart multiPart) {
    return multiPart;
}

9.3.3.2. Injecting with @FormDataParam

If you just need to bin the named body part(s) of a multipart/form-data request entity body to a resource method parameter you can use @FormDataParam annotation.

This annotation in conjunction with the media type multipart/form-data should be used for submitting and consuming forms that contain files, non-ASCII data, and binary data.

The type of the annotated parameter can be one of the following (for more detailed description see javadoc to @FormDataParam):

  • FormDataBodyPart - The value of the parameter will be the first named body part or null if such a named body part is not present.

  • A List or Collection of FormDataBodyPart. The value of the parameter will one or more named body parts with the same name or null if such a named body part is not present.

  • FormDataContentDisposition - The value of the parameter will be the content disposition of the first named body part part or null if such a named body part is not present.

  • A List or Collection of FormDataContentDisposition. The value of the parameter will one or more content dispositions of the named body parts with the same name or null if such a named body part is not present.

  • A type for which a message body reader is available given the media type of the first named body part. The value of the parameter will be the result of reading using the message body reader given the type T, the media type of the named part, and the bytes of the named body part as input.

    If there is no named part present and there is a default value present as declared by @DefaultValue then the media type will be set to text/plain. The value of the parameter will be the result of reading using the message body reader given the type T, the media type text/plain, and the UTF-8 encoded bytes of the default value as input.

    If there is no message body reader available and the type T conforms to a type specified by @FormParam then processing is performed as specified by @FormParam, where the values of the form parameter are String instances produced by reading the bytes of the named body parts utilizing a message body reader for the String type and the media type text/plain.

    If there is no named part present then processing is performed as specified by @FormParam.

Example 9.50. Use of @FormDataParam annotation

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String postForm(
    @DefaultValue("true") @FormDataParam("enabled") boolean enabled,
    @FormDataParam("data") FileData bean,
    @FormDataParam("file") InputStream file,
    @FormDataParam("file") FormDataContentDisposition fileDisposition) {

    // ...
}

In the example above the server consumes a multipart/form-data request entity body that contains one optional named body part enabled and two required named body parts data and file.

The optional part enabled is processed as a boolean value, if the part is absent then the value will be true.

The part data is processed as a JAXB bean and contains some meta-data about the following part.

The part file is a file that is uploaded, this is processed as an InputStream. Additional information about the file from the Content-Disposition header can be accessed by the parameter fileDisposition.

Tip

@FormDataParam annotation can be also used on fields.