Integrating Gson into a JAX-RS based application
November 2, 2012 | 3 min ReadCreating 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,
MessageBodyReader {
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 type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap 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 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