JSON in Query Params or How to Inject Custom Java Types via JAX-RS Parameter Annotations

Although I am not a big fan of sending JSON in other places than in the message body as the entity, for example in query parameter in case of requests, it’s not a rare use-case and the way how it can be solved in JAX-RS 2.0 gives me a nice opportunity to illustrate usage of two new interfaces, ParamConverterProvider and ParamConverter. You can then re-use this approach to inject other media-types or formats your application relies on via @*Param annotations (@MatrixParam, @QueryParam, @PathParam, @CookieParam, @HeaderParam).

Use-Case: Sending JSON as Query Parameter

All snippets listed in this article are taken from jersey-param-converters (see Example) available at GitHub.

Java POJO <-> JSON

In the examples below I’ll be using the following Entity class as an input and also as output in my resource methods:

In JSON format an instance of this class would, more-or-less, look like:

or

Client

Let’s assume that you want to send a JSON string as a query or a header parameter to your REST service. The client code, in case of query parameter, could look like this:

To make sure the resulting JSON is properly encoded in URI I am using UriComponent helper class from Jersey to encode JSON as QUERY_PARAM_SPACE_ENCODED type.

The actual request would look like:

In case we want to send our JSON as a header value the client code is little bit simpler:

And the request:

Server

We want the resource class, handling requests described above, to be as simple as possible. No custom conversion of JSON strings to Java objects in our resource methods. JAX-RS runtime should do this for us. So, all we need to do is write a resource class similar to:

As you can see there are two @GET methods. Both are returning injected entity, transformed to JSON, to the client. The first one is trying to inject value of a query parameter named entity (see @QueryParam) into the method parameter of type Entity. If JAX-RS runtime cannot find such a query parameter the value from @DefaultValue is used to create Entity instead. The second one is, similarly, injecting value of a request header named Entity (see @HeaderParam).

From both of the resource methods we’d like to return a response similar to:

OK, enough of what we want to achieve, let’s take a look at how to actually do it.

Param Converters (and Providers)

To make the injection work we need to create implementations of two interfaces from JAX-RS 2.0, ParamConverterProvider and ParamConverter. With the first one we simply tell the runtime whether we’re able to convert String to a particular type and with the second one we’re doing the actual conversion.

So, for our use-case of converting JSON into custom Java types we’re going to create a slightly more powerful version of converter provider and converter itself. For the purpose of converting we’ll be using Jackson library, particularly it’s ObjectMapper class. As you might know it’s possible to pass your own instances of ObjectMapper to Jackson message body providers via custom ContextResolver (see below). We want to use this mechanism even with converting JSON to parameters so both approaches (converting request entity and converting parameters) have the configuration of ObjectMapper in one place:

And our implementations of ParamConverterProvider and ParamConverter are:

As you can see it’s not complicated at all and hence it’s pretty useful. In the first part (lines 12–17) I am making sure that the JSON<->Java message body reader (the one from Jackson as I didn’t register any other JSON providers) is capable of unmarshalling JSON into Java POJO of given type. If this is the case I know that I can also convert JSON into this Java POJO and inject it via parameter annotations.

In the second part (lines 20–24) I am looking for a custom ObjectMapper. If there is one I’ll use it during conversion otherwise I’ll create a default one.

And finally on lines 27–46 I am creating new ParamConverter which is actually converting the JSON string into an instance of given type (line 32). I could use the retrieved message body reader to the job and the result would be the same, of course. But I think my approach is better to illustrate how this mechanism really works.

Default JAX-RS Param Converters

In some (basic) cases it’s not necessary to create your own param converters (and providers) to inject parameters via @*Param annotations (@MatrixParam, @QueryParam, @PathParam, @CookieParam, @HeaderParam) because JAX-RS 2.0 implementations have to support the following types:

  1. Primitive types.
  2. Types that have a constructor that accepts a single String argument.
  3. Types that have a static method named valueOf or fromString with a single String argument that return an instance of the type. If both methods are present then valueOf MUST be used unless the type is an enum in which case fromString MUST be used.
  4. List<T>, Set<T>, or SortedSet<T>, where T satisfies 2 or 3 above.

This basically tells us that when there are only a few classes which we want to inject it’s sufficient to create static methods fromString / valueOf in these classes (or make sure the classes have a constructor which accepts one string argument) and we can use them as injection targets.

Additional Jersey Param Converters

In addition to converters mentioned above Jersey provides two more param converters for the following types:

  1. Date – for following date format patterns
    • EEE, dd MMM yyyy HH:mm:ss zzz (RFC 1123)
    • EEEE, dd-MMM-yy HH:mm:ss zzz (RFC 1036)
    • EEE MMM d HH:mm:ss yyyy (ANSI C)
  2. JAXB bean – has to be annotated either with @XmlRootElement or @XmlType

The Date param converter has the highest priority and the JAXB one the lowest priority from all the default converters. This means than in case you have a JAXB class like:

The injected JaxbBean would be created using fromString JAX-RS default param converter and not the JAXB one.

Example

As I mentioned earlier there is an example you can try (both client and server sides) available on GitHub: jersey-param-converters. It’s a simple JAX-RS web-application with 3 test-cases that are representing the client side story.

Further Reading

Jersey User Guide – Chapter 3. Parameter Annotations (@*Param)