JAX-RS Jersey Gson Serializer and Deserializer

This tutorial shows how to use Gson to marshal and unmarshal a Java object to and from JSON representation. You could also use the default implementations like: Jackson, JSONP or MOXy. But the power of using Gson, is that it doesn’t require any annotations on your classes and thus can work with any third party libraries, which you don’t have the source code of.

Jersey does not support Gson by default. But in the following example we show you how you can support it. We make efficient use of the MessageBodyReader and MessageBodyWriter to support Gson to make the conversions between Java and JSON.

Dependencies

First, make sure that the Gson library is on your class path. When using maven, you can add the following maven dependencies.

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.22.2</version>
</dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.6.2</version>
</dependency>

Custom JAX-RS Jersey GsonProvider

Next, we are creating a Gson Provider to serialize and deserialize a JSON String to and from a Java Object.

The MessageBodyReader is used to deserialize the JSON string into a Java object. We can use the isReadable() method to manage whether the class is eligible for deserialization. Using the readFrom() method, we can convert the InputStream into a Java object.

The MessageBodyWriter is used to serialize a Java object into a JSON String representation. We can use the isWriteable() method to manage whether the class is eligible for serialization. Using the writeTo method, we can serialize the Java object into a JSON representation. Note that we also injected the UriInfo, this allows us to inspect the query parameters. We search for a query parameter named “pretty-print”. When this query parameter is found, we use a different Gson object to return the ‘pretty’ JSON String, which is more human readable.

Finally, we annotate the GsonProvider with the @Provider annotation, in order to tell Jersey to automatically register this class. The @Produces and @Consumes annotations are used to tell the provider which media types it supports.

package com.memorynotfound.jaxrs.gson;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class GsonProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {

    private static final String PRETTY_PRINT = "pretty-print";

    private final Gson gson;
    private final Gson prettyGson;

    @Context
    private UriInfo ui;

    public GsonProvider() {
        GsonBuilder builder = new GsonBuilder()
                .serializeNulls()
                .enableComplexMapKeySerialization();

        this.gson = builder.create();
        this.prettyGson = builder.setPrettyPrinting().create();
    }

    @Override
    public boolean isReadable(Class<?> type, Type genericType,
                              Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public T readFrom(Class<T> type, Type genericType, Annotation[] annotations,
                      MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
                      InputStream entityStream) throws IOException, WebApplicationException {

        InputStreamReader reader = new InputStreamReader(entityStream, "UTF-8");
        try {
            return gson.fromJson(reader, type);
        } finally {
            reader.close();
        }
    }

    @Override
    public boolean isWriteable(Class<?> type, Type genericType,
                               Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public long getSize(T t, Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations,
                        MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
                        OutputStream entityStream) throws IOException, WebApplicationException {

        PrintWriter printWriter = new PrintWriter(entityStream);
        try {
            String json;
            if (ui.getQueryParameters().containsKey(PRETTY_PRINT)){
                json = prettyGson.toJson(t);
            } else {
                json = gson.toJson(t);
            }
            printWriter.write(json);
            printWriter.flush();
        } finally {
            printWriter.close();
        }
    }
}

We are configuring Jersey without a web.xml servlet descriptor. By using the @ApplicationPath annotation, Jersey is automatically configured and searches automatically for any other @Provider annotations.

package com.memorynotfound.jaxrs.gson;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import java.util.HashMap;
import java.util.Map;

@ApplicationPath("/api")
public class ApplicationConfig extends Application {

}

The following rest resource returns a simple Health object.

package com.memorynotfound.jaxrs.gson;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/health")
public class HealthResource {

    @GET
    public Health getHealth(){
        return new Health("hostname", "123.123.123.123");
    }

}

The following Java object will be serialized and deserialized using Gson. The power of Gson is that your object doesn’t need any special annotations in order to make it eligible for JSON processing. This makes it ideal to work with third party libraries.

package com.memorynotfound.jaxrs.gson;

import java.lang.management.ManagementFactory;

public class Health {

    private String hostname;
    private String ip;
    private long startTime;
    private long upTime;

    public Health(String hostname, String ip) {
        this.hostname = hostname;
        this.ip = ip;
        this.startTime = ManagementFactory.getRuntimeMXBean().getStartTime();
        this.upTime = ManagementFactory.getRuntimeMXBean().getUptime();
    }
}

Demo

URL: http://localhost:8081/jaxrs-gson/api/health

jax-rs jersey gson serializer deserializer

URL: http://localhost:8081/jaxrs-gson/api/health?pretty-print

jax-rs jersey gson serializer deserializer

References

Download

You may also like...