Integrating Gson into a JAX-RS based application

Jersey yellow Integrating Gson into a JAX RS based applicationCreating modern applications often involves consuming REST based web services. One of the more popular ways to publish a REST service in Java is the JAX-RS (Jersey) specification. It allows you to very easily enhance your REST resources with Java annotations.

In many cases REST is used in combination with the JSON document format. In order to produce JSON via JAX-RS, one could simply create a JSON document as a String and let the REST service return it in the response body. Although this approach is acceptable it is much more elegant to have a serialization mechanism that turns a Java POJO into a JSON document automatically, thereby hiding the JSON syntax altogether.

The reference implementation for JAX-RS is the Jersey project, which is able to serialize Java POJOs via the Jackson library out of the box. But it is also relatively easy to use a different framework for the serialization process, as you’ll see in this post.

JAX-RS allows you to use classpath scanning to discover Java classes that should be published as REST services. The same classpath discovery mechanism can be used to contribute a different serialization framework. It is as simple as implementing the two interfaces javax.ws.rs.ext.MessageBodyWriter and javax.ws.rs.ext.MessageBodyReader, and annotating the implementing class with @Provider.

The following snippet is an implementation of a GsonMessageBodyHandler that implements the two interfaces and offers it to Jersey.

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
 
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
 
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
 
@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public final class GsonMessageBodyHandler implements MessageBodyWriter<Object>,
    MessageBodyReader<Object> {
 
  private static final String UTF_8 = "UTF-8";
 
  private Gson gson;
 
  private Gson getGson() {
    if (gson == null) {
      final GsonBuilder gsonBuilder = new GsonBuilder();
      gson = gsonBuilder.create();
    }
    return gson;
  }
 
  @Override
  public boolean isReadable(Class<?> type, Type genericType,
      java.lang.annotation.Annotation[] annotations, MediaType mediaType) {
    return true;
  }
 
  @Override
  public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws Exception, ApplicationException {
    InputStreamReader streamReader = new InputStreamReader(entityStream, UTF_8);
    try {
      Type jsonType;
      if (type.equals(genericType)) {
        jsonType = type;
      } else {
        jsonType = genericType;
      }
      return getGson().fromJson(streamReader, jsonType);
    } finally {
      streamReader.close();
    }
  }
 
  @Override
  public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return true;
  }
 
  @Override
  public long getSize(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return -1;
  }
 
  @Override
  public void writeTo(Object object, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
    OutputStreamWriter writer = new OutputStreamWriter(entityStream, UTF_8);
    try {
      Type jsonType;
      if (type.equals(genericType)) {
        jsonType = type;
      } else {
        jsonType = genericType;
      }
      getGson().toJson(object, jsonType, writer);
    } finally {
      writer.close();
    }
  }
}

Leave us a comment – I’d be interested to hear which frameworks you’re using and your experiences with the implementation.

Find me on Google+ - gplus.to/mpost

4 Responses to “Integrating Gson into a JAX-RS based application”

  1. Amar says:

    Awesome. Jackson is so tightly coupled with jersey that we cannot have backward compatibility. Nice to see this implementation.

  2. Mike Dias says:

    This is great! Can you post a sample configuration to enable this feature in a JAX-RS application?

  3. Moritz Post says:

    @Mike

    All you have to do is to place the Handler into a package that is scanned by the JAX-RS classpath scanner.

  4. Mike Dias says:

    @Moritz Thanks for the answer! The keyword “scanned” help me to find the solution.

    In the RestEasy, the following parameters are required in “web.xml”:

    resteasy.scan.providers
    true

    resteasy.use.builtin.providers
    false

    So, your provider works perfectly! =)

4 responses so far

Written by . Published in Categories: EclipseSource News

Author:
Published:
Nov 2nd, 2012
Follow:

Google+