Table of Contents
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:
MOXy - JSON binding support via MOXy is a default and preferred way of supporting JSON binding in your Jersey applications since Jersey 2.0. When JSON MOXy module is on the class-path, Jersey will automatically discover the module and seamlessly enable JSON binding support via MOXy in your applications. (See Section 4.3, “Auto-Discoverable Features”.)
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.
POJO support represents the easiest way to convert your Java Objects to JSON and back.
Media modules that support this approach are MOXy, Jackson, and Java API for JSON Binding (JSON-B)
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
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 (including Jakarta EE 8).
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.
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 Jakarta EE 8 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.
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.38-SNAPSHOT</version> </dependency>
If you're not using Maven make sure to have all needed dependencies (see jersey-media-moxy) on the classpath.
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 its Feature
s
(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):
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:
MoxyJsonConfig#property(java.lang.String, java.lang.Object) - sets a property value for both Marshaller and Unmarshaller.
MoxyJsonConfig#marshallerProperty(java.lang.String, java.lang.Object) - sets a property value for Marshaller.
MoxyJsonConfig#unmarshallerProperty(java.lang.String, java.lang.Object) - sets a property value for Unmarshaller.
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_OUTPUT | false |
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);
Jersey provides a JSON MOXy example on how to use MOXy to consume/produce JSON.
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.38-SNAPSHOT</version> </dependency>
If you're not using Maven make sure to have all needed dependencies (see jersey-media-json-processing) on the class-path.
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 its
Feature
s (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);
Jersey provides a JSON Processing example on how to use JSON-Processing to consume/produce JSON.
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.38-SNAPSHOT</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.38-SNAPSHOT</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.
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.
Since the 2.36 version of Jersey it is possible to filter (include/exclude) Jackson modules by properties
CommonProperties.JSON_JACKSON_DISABLED_MODULES and CommonProperties.JSON_JACKSON_ENABLED_MODULES
(with their client/server derivatives). If the CommonProperties.JSON_JACKSON_ENABLED_MODULES property is used,
only those named modules will be used for JSON processing. On the other hand if the CommonProperties.JSON_JACKSON_DISABLED_MODULES
property is used, those listed modules will be explicitly excluded from processing while other (not listed) will remain. Please note that
the JaxbAnnotationModule
module is always excluded from processing and this is not configurable.
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);
Jersey provides JSON Jackson (2.x) example and JSON Jackson (1.x) example showing how to use Jackson to consume/produce JSON.
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).
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.38-SNAPSHOT</version> </dependency>
If you're not using Maven make sure to have all needed dependencies (see jersey-media-json-jettison) on the classpath.
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.
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"], ... } }
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" } } } }
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);
Jersey provides an JSON Jettison example on how to use Jettison to consume/produce JSON.
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.
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.
Jersey uses Yasson for JSON Binding (JSR-367) implementation.
To use JSON-B as your JSON provider you need to add jersey-media-json-binding
module to your
pom.xml
file:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-binding</artifactId> <version>2.38-SNAPSHOT</version> </dependency>
If you're not using Maven make sure to have all needed dependencies (see jersey-media-json-binding) on the classpath.
As stated in Section 4.3, “Auto-Discoverable Features” JSON-Binding media module is one of the
modules where you don't need to explicitly register its
Feature
s (JsonBindingFeature
) in your client/server
Configurable as this feature is automatically discovered and registered when you add
jersey-media-json-binding
module to your classpath.
To use custom preconfigured JSON-B, it is simply possible to register
a ContextResolver<T>
for Jsonb
in your Configurable
(client/server) and configure JsonbConfig.
Example 9.31. ContextResolver<Jsonb>
@Provider public class JsonbContextResolver implements ContextResolver<Jsonb> { @Override public Jsonb getContext(Class>?< type) { JsonbConfig config = new JsonbConfig(); // configure JsonbConfig ... return JsonbBuilder.create(config); } }
Example 9.32. Register the feature and ContextResolver<Jsonb>
ClientBuilder.newClient(new ClientConfig() // The line below that registers JSON-Binding feature can be // omitted if FEATURE_AUTO_DISCOVERY_DISABLE is not disabled. .register(JsonBindingFeature.class) .register(JsonbContextResolver.class) );
Example. You can take a look at a provided JSON-B example..
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.
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.33. 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.
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.34. Planet class
@XmlRootElement public class Planet { public int id; public String name; public double radius; }
Example 9.35. 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.36. 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.
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.37. 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.38. 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));
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.39. 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
register them in the client config (client does not do anything like package scanning done by server).
Example 9.40. Using Provider with JAX-RS client
ClientConfig config = new ClientConfig(); config.register(PlanetJAXBContextProvider.class); Client client = ClientBuilder.newClient(config);
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.41. Add jersey-media-moxy
dependency.
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-moxy</artifactId> <version>2.38-SNAPSHOT</version> </dependency>
Example 9.42. Register the MoxyXmlFeature
class.
final ResourceConfig config = new ResourceConfig() .packages("org.glassfish.jersey.examples.xmlmoxy") .register(MoxyXmlFeature.class);
Example 9.43. 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. ));
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.
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.38-SNAPSHOT</version> </dependency>
If you're not using Maven make sure to have all needed dependencies (see jersey-media-multipart) on the class-path.
Before you can use capabilities of the jersey-media-multipart
module in your client/server code, you
need to register MultiPartFeature.
Example 9.44. Building client with MultiPart feature enabled.
final Client client = ClientBuilder.newBuilder() .register(MultiPartFeature.class) .build();
Example 9.45. 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)
Jersey provides a Multipart Web Application Example on how to use multipart features.
MultiPart class (or it's subclasses) can be used as an entry point to use
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.46. 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.47. 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.48. 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.49. 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.50. 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()));
Do not use ApacheConnectorProvider
nor GrizzlyConnectorProvider
neither JettyConnectorProvider
connector implementations with Jersey Multipart
features. See Header modification issue warning for more details.
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.
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.51. Resource method using MultiPart
as input parameter / return value.
@POST @Produces("multipart/mixed") public MultiPart post(final FormDataMultiPart multiPart) { return multiPart; }
If you just need to bind 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 be 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 be 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.52. 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
.
@FormDataParam
annotation can be also used on fields.