Table of Contents
Jersey JSON support comes as a set of extension modules where each of these modules contains an implementation of aFeature that needs to be registered into yourConfigurable 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. (SeeSection 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 areMOXy,Jackson, andJava 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
@XmlRootElementpublic 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 ofMyJaxbBean, 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
@XmlRootElementpublic class MyJaxbBean { @XmlElement(name="king") public String name; @XmlTransient public int age; // several lines removed}Media modules that support this approach areMOXy,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 Jakarta EE 9 and later. Another such JSON parsing/processing API is provided by Jettison framework (which is also supported in jakartified environment). Both APIs provide a low-level access to producing and consuming JSON data structures. By adopting this low-level approach you would be working withJsonObject (orJSONObject respectively) and/orJsonArray (orJSONArray 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 equivalentJsonObject/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 9 JSON-Processing API.
Example 9.5. Constructing aJsonObject (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 aJSONObject (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 areJava API for JSON Processing (JSON-P) andJettison. Unless you have a strong reason for using the non-standardJettison API, we recommend you to use the new standardJava API for JSON Processing (JSON-P) API instead.
To use MOXy as your JSON provider you need to addjersey-media-moxy module to yourpom.xml file:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-moxy</artifactId> <version>4.0.0</version></dependency>
If you're not using Maven make sure to have all needed dependencies (seejersey-media-moxy) on the classpath.
As stated in theSection 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 itsFeatures (MoxyJsonFeature) in your client/serverConfigurable as this feature is automatically discovered and registered when you addjersey-media-moxy module to your class-path.
The auto-discoverablejersey-media-moxy module defines a few properties that can be used to control the automatic registration ofMoxyJsonFeature (besides the genericCommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE an the its client/server variants):
A manual registration of any other Jersey JSON provider feature (except forJava API for JSON Processing (JSON-P)) disables the automated enabling and configuration ofMoxyJsonFeature.
To configureMessageBodyReader<T>s /MessageBodyWriter<T>s provided by MOXy you can simply create an instance ofMoxyJsonConfig 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 makeMoxyJsonConfig visible for MOXy you need to create and registerContextResolver<T> in your client/server code.
Example 9.8. CreatingContextResolver<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 underlyingMOXyJsonProvider is to set them directly into yourConfigurable instance (see an example below). These are overwritten by properties set into theMoxyJsonConfig.
Example 9.9. Setting properties for MOXy providers intoConfigurable
new ResourceConfig() .property(MarshallerProperties.JSON_NAMESPACE_SEPARATOR, ".") // further configuration
There are some properties for which Jersey sets the default value whenMessageBodyReader<T> /MessageBodyWriter<T> from MOXy is used and they are:
Table 9.1. Default property values for MOXyMessageBodyReader<T> /MessageBodyWriter<T>
jakarta.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 aJSON MOXy example on how to use MOXy to consume/produce JSON.
To use JSON-P as your JSON provider you need to addjersey-media-json-processing module to yourpom.xml file:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-processing</artifactId> <version>4.0.0</version></dependency>
If you're not using Maven make sure to have all needed dependencies (seejersey-media-json-processing) on the class-path.
As stated inSection 4.3, “Auto-Discoverable Features” JSON-Processing media module is one of the modules where you don't need to explicitly register itsFeatures (JsonProcessingFeature) in your client/serverConfigurable as this feature is automatically discovered and registered when you addjersey-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 ofJsonProcessingFeature (besidesCommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE and the like):
To configureMessageBodyReader<T>s /MessageBodyWriter<T>s provided by JSON-P you can simply add values for supported properties into theConfiguration instance (client/server). Currently supported are these properties:
JsonGenerator.PRETTY_PRINTING ("jakarta.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 aJSON Processing example on how to use JSON-Processing to consume/produce JSON.
To use Jackson 2.x as your JSON provider you need to addjersey-media-json-jackson module to yourpom.xml file:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jackson</artifactId> <version>4.0.0</version></dependency>
If you're not using Maven make sure to have all needed dependencies (seejersey-media-json-jackson) on the classpath.
Note that namespace for Jackson 2.x is (com.fasterxml.jackson).
Jackson JSON processor could be controlled via providing a custom Jackson 2ObjectMapper 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 yourObjectMapper instance into your Jersey application.
Since the 2.36 version of Jersey it is possible to filter (include/exclude) Jackson modules by propertiesCommonProperties.JSON_JACKSON_DISABLED_MODULES andCommonProperties.JSON_JACKSON_ENABLED_MODULES (with their client/server derivatives). If theCommonProperties.JSON_JACKSON_ENABLED_MODULES property is used, only those named modules will be used for JSON processing. On the other hand if theCommonProperties.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 theJaxbAnnotationModule 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 registerJacksonFeature and aContextResolver<T> forObjectMapper, if needed, in yourConfigurable (client/server).
Example 9.14. ContextResolver<ObjectMapper>
@Providerpublic 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 theJSON-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 providesJSON Jackson (2.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 anJettisonConfig instance. The instance could be then further used to create aJettisonJaxbContext, which serves as a main configuration point in this area. To pass your specializedJettisonJaxbContext to Jersey, you will finally need to implement a JAXBContextContextResolver<T> (see below).
To use Jettison as your JSON provider you need to addjersey-media-json-jettison module to yourpom.xml file:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-jettison</artifactId> <version>4.0.0</version></dependency>
If you're not using Maven make sure to have all needed dependencies (seejersey-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
@XmlRootElementpublic 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
@XmlRootElementpublic 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 withid=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 atJettisonConfig.
If you need to deal with various XML namespaces, you will find Jettisonmapped notation pretty useful. Lets define a particular namespace forid 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 basedmapped 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, thatid item becameexample.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 basedmapped 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 usingbadgerfish notation could be built with
JettisonConfig.badgerFish().build()
and the JSON output JSON will be as follows.
Example 9.24. JSON expression produced usingbadgerfish 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 registerJettisonFeature and aContextResolver<T> forJAXBContext (if needed) in yourConfigurable (client/server).
Example 9.25. ContextResolver<ObjectMapper>
@Providerpublic 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 anJSON Jettison example on how to use Jettison to consume/produce JSON.
Jersey provides out-of-the-box support forJSONP - 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> forapplication/json media type, which also accepts the return type of the resource method, needs to be registered (seeJSON section of this chapter).
User's request has to containAccept 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 theJaxbBean looks like:
Example 9.29. JaxbBean for @JSONP example
@XmlRootElementpublic 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 aGET request withAccept header set toapplication/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 tocallback as you can see in the previous example.@JSONP has two parameters that can be configured:callback andqueryParam.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 ofqueryParam 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 overcallback 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 providedJSON with Padding example.
Jersey usesYasson for JSON Binding (JSR-367) implementation.
To use JSON-B as your JSON provider you need to addjersey-media-json-binding module to yourpom.xml file:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-json-binding</artifactId> <version>4.0.0</version></dependency>
If you're not using Maven make sure to have all needed dependencies (seejersey-media-json-binding) on the classpath.
As stated inSection 4.3, “Auto-Discoverable Features” JSON-Binding media module is one of the modules where you don't need to explicitly register itsFeatures (JsonBindingFeature) in your client/serverConfigurable as this feature is automatically discovered and registered when you addjersey-media-json-binding module to your classpath.
To use custom preconfigured JSON-B, it is simply possible to register aContextResolver<T> forJsonb in yourConfigurable (client/server) and configureJsonbConfig.
Example 9.31. ContextResolver<Jsonb>
@Providerpublic 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 providedJSON-B example..
Apart from usingContextResolver<T> for configuring directly the specific JSON provider classes, Jersey supports additional configuration properties prefixed byJSON that are available inSection A.6, “Jersey configuration properties for message & entity processing”
As you probably already know, Jersey usesMessageBodyWriter<T>s andMessageBodyReader<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 andDocument. 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 havehelloworld 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 toHelloWorldResource.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;} BothMessageBodyWriter<T> andMessageBodyReader<T> are used in this case, all we need is aPOST 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 examplecurl:
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 isJAXB 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 classPlanet and service which produces "Planets".
Example 9.34. Planet class
@XmlRootElementpublic 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 onPlanet class, particularly@XmlRootElement. This is an JAXB annotation which maps java classes to XML elements. We don't need to specify anything else, becausePlanet 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 toGET /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-createdWebTarget object which points to our applications context root and we simply add path (in our case itsplanet), accept header (not mandatory, but service could provide different content based on this header; for exampletext/html can be served for web browsers) and at the end we specify that we are expectingPlanet class viaGET 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 ofPlanet, which can look likePlanet{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 byxjc 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 onPlanet 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 withJAXBElement. This is because now you need to explicitly set element name forPlanet class XML representation. Client side is even more complicated than server side because you can't doJAXBElement<Planet> so JAX-RS client API provides way how to workaround it by declaring subclass ofGenericType<T>.
Example 9.38. Client side - JAXBElement
// GETGenericType<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);// POSTplanet = new Planet();// ...webTarget.path("planet").post(new JAXBElement<Planet>(new QName("planet"), Planet.class, planet));In some scenarios you can take advantage of using customJAXBContext. CreatingJAXBContext 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 toJAXBContext, for example to set a different class loader.
Example 9.39. PlanetJAXBContextProvider
@Providerpublic 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 simpleJAXBContext 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 useMOXy as your JAXB implementation instead of JAXB RI you have two options. You can either use the standard JAXB mechanisms to define theJAXBContextFactory from which aJAXBContext instance would be obtained (for more on this topic, read JavaDoc onJAXBContext) or you can addjersey-media-moxy module to your project and register/configureMoxyXmlFeature class/instance in theConfigurable.
Example 9.41. Addjersey-media-moxy dependency.
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-moxy</artifactId> <version>4.0.0</version></dependency>
Example 9.42. Register theMoxyXmlFeature class.
final ResourceConfig config = new ResourceConfig() .packages("org.glassfish.jersey.examples.xmlmoxy") .register(MoxyXmlFeature.class);Example 9.43. Configure and register anMoxyXmlFeature 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. )); Apart from usingContextResolver<T> for configuring directly the specific JSON provider classes, Jersey supports additional configuration properties prefixed byJAXB andXML that are available inSection A.6, “Jersey configuration properties for message & entity processing”
The classes in this module provide an integration ofmultipart/* 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 sameMessageBodyReader<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:
TheMIME-Version: 1.0 HTTP header is included on generated responses. It is accepted, but not required, on processed requests.
AMessageBodyReader<T> implementation for consuming MIME MultiPart entities.
AMessageBodyWriter<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 appropriateboundary parameter on a generatedContent-Type header, if not already present.
For more information refer toMulti Part.
To use multipart features you need to addjersey-media-multipart module to yourpom.xml file:
<dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-multipart</artifactId> <version>4.0.0</version></dependency>
If you're not using Maven make sure to have all needed dependencies (seejersey-media-multipart) on the class-path.
Prior to Jersey 3.1.0, before you can use the capabilities of thejersey-media-multipart module in your client/server code, you need to registerMultiPartFeature. The multipart feature is supported by Jakarta RESTful Web Services 3.1 multipart API. From Jersey 3.1.0 on, theMultiPartFeature is no longer required to be registered and it is registered automatically.
Jersey provides aMultipart Web Application Example on how to use multipart features.
MultiPart class (or it's subclasses) can be used as an entry point to usejersey-media-multipart module on the client side. This class represents aMIME multipart message and is able to hold an arbitrary number ofBodyParts. Default media type ismultipart/mixed forMultiPart entity andtext/plain forBodyPart.
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 amultiPartEntity to the server the entity withContent-Type header in HTTP message would look like:
Example 9.45. MultiPart entity in HTTP message.
Content-Type: multipart/mixed; boundary=Boundary_1_829077776_1369128119878--Boundary_1_829077776_1369128119878Content-Type: text/plainhello--Boundary_1_829077776_1369128119878Content-Type: application/xml<?xml version="1.0" encoding="UTF-8" standalone="yes"?><jaxbBean><value>xml</value></jaxbBean>--Boundary_1_829077776_1369128119878Content-Type: application/json{"value":"json"}--Boundary_1_829077776_1369128119878-- When working with forms (e.g. media typemultipart/form-data) and various fields in them, there is a more convenient class to be used -FormDataMultiPart. It automatically sets the media type for theFormDataMultiPart entity tomultipart/form-data andContent-Disposition header toFormDataBodyPart 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 usingFormDataMultiPart instead ofFormDataBodyPart you can take a look at theFormDataMultiPart 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_1369143433608Content-Type: text/plainContent-Disposition: form-data; name="hello"hello--Boundary_1_511262261_1369143433608Content-Type: application/xmlContent-Disposition: form-data; name="xml"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><jaxbBean><value>xml</value></jaxbBean>--Boundary_1_511262261_1369143433608Content-Type: application/jsonContent-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 fromorg.glassfish.jersey.jersey.media.multipart package, such asFileDataBodyPart orStreamDataBodyPart.
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())); Do not useApacheConnectorProvider norGrizzlyConnectorProvider neitherJettyConnectorProvider connector implementations with Jersey Multipart features. SeeHeader modification issue warning for more details.
EntityPart interface can be used as an entry point to usejersey-media-multipart module on the client side. This class represents multipart message is able to hold an arbitrary number ofEntityParts. Default media type ismultipart/form-data.
Example 9.49. UsingEntityPart.Builder for building an Entity
final List<EntityPart> multiPartEntity = new List<>();list.add(EntityPart.withName("part-01").content("hello").build());list.add(EntityPart.withName("part-01").content(new JaxbBean("xml")).mediaType(MediaType.APPLICATION_XML_TYPE).build()); //same namelist.add(EntityPart.withName("part-02").content(new JaxbBean("json")).mediaType(MediaType.APPLICATION_JSON_TYPE).build()); //other namefinal GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};final Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);final WebTarget target = // Create WebTarget.final Response response = target.request().post(entity);The common use-case for many users is sending files from client to server. It is also covered byEntityPart.Builder.
Example 9.50. EntityPart - sending files.
// MediaType of the body part will be derived from the file.final List<EntityPart> multiPartEntity = new List<>();list.add(EntityPart.withFileName("file001.txt").content(Files.newInputStream(Path.of("file001.txt"))).build());list.add(EntityPart.withFileName("mypom.xml").content(Files.newInputStream(Path.of("pom.xml"))).build());final GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {};final Entity entity = Entity.entity(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE);final WebTarget target = // Create WebTarget.final Response response = target.request().post(entity);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 wholeMultiPart entity.
Injecting particular parts of aform-data multipart request via@FormDataParam annotation.
Working withMultiPart types is no different from injecting/returning other entity types. Jersey providesMessageBodyReader<T> for reading the request entity and injecting this entity into a method parameter of a resource method andMessageBodyWriter<T> for writing output entities. You can expect that eitherMultiPart orFormDataMultiPart (multipart/form-data media type) object to be injected into a resource method.
Example 9.51. Resource method usingMultiPart 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 amultipart/form-data request entity body to a resource method parameter you can use@FormDataParam annotation.
This annotation in conjunction with the media typemultipart/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 ornull if such a named body part is not present.
AList orCollection ofFormDataBodyPart. The value of the parameter will be one or more named body parts with the same name ornull 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 ornull if such a named body part is not present.
AList orCollection ofFormDataContentDisposition. The value of the parameter will be one or more content dispositions of the named body parts with the same name ornull 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 typeT, 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 totext/plain. The value of the parameter will be the result of reading using the message body reader given the typeT, the media typetext/plain, and the UTF-8 encoded bytes of the default value as input.
If there is no message body reader available and the typeT conforms to a type specified by@FormParam then processing is performed as specified by@FormParam, where the values of the form parameter areString instances produced by reading the bytes of the named body parts utilizing a message body reader for theString type and the media typetext/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 amultipart/form-data request entity body that contains one optional named body partenabled and two required named body partsdata andfile.
The optional partenabled is processed as aboolean value, if the part is absent then the value will betrue.
The partdata is processed as a JAXB bean and contains some meta-data about the following part.
The partfile is a file that is uploaded, this is processed as anInputStream. Additional information about the file from theContent-Disposition header can be accessed by the parameterfileDisposition.
@FormDataParam annotation can be also used on fields.
There are multiple options that can be used when configuring the multipart. SeeMultiPartProperties orSection A.13, “Multipart configuration properties” for the possibilities.
The options can set in a configuration file specified by theMultiPartProperties.MULTI_PART_CONFIG_RESOURCE property. That is the standard Java properties file.
Or the options can be set programmatically, by registeringContextResolver<MultiPartProperties>. For instance:
ResourceConfig resourceConfig = new ResourceConfig();resourceConfig.register(new MultiPartProperties().bufferThreshold(65535).maxParts(2).resolver());
UsingEntityPart on the server side is similar to the client side. Jakarta REST specification allows for returning aResponse or aList ofEntityParts.
Receiving theEntityParts can be done either using@FormParam annotations andEntityPart,InputStream orString data-types, or using aList ofEntityParts.
Example 9.53. Use of@FormParam annotation withEntityPartInputStream andString types and returning aResponse
@POST@Path("/postFormVarious")public Response postFormVarious(@FormParam("name1") EntityPart part1, @FormParam("name2") InputStream part2, @FormParam("name3") String part3) throws IOException { final List<EntityPart> list = new LinkedList<>(); list.add(EntityPart.withName(part1.getName()) .content(part1.getContent(String.class) + new String(part2.readAllBytes()) + part3) .mediaType(MediaType.TEXT_PLAIN_TYPE) .build()); final GenericEntity<List<EntityPart>> genericEntity = new GenericEntity<>(list) {}; return Response.ok(genericEntity, MediaType.MULTIPART_FORM_DATA_TYPE).build();}Example 9.54. Receiving aList ofEntityParts
@POST@Path("/postListForm")public String postEntityPartForm(@FormParam("part-0x") List<EntityPart> part) throws IOException { final String entity = part.get(0).getContent(String.class) + part.get(1).getContent(String.class); return entity;}Example 9.55. Returning aList ofEntityParts
@GET@Produces(MediaType.MULTIPART_FORM_DATA)@Path("/getList")public List<EntityPart> getList() throws IOException { final List<EntityPart> list = new LinkedList<>(); list.add(EntityPart.withName("name1").content("data1").build()); return list;}