Usando Gson con JAX-RS

La codificación más habitual de los servicios web RESTful es JSON. La implementación de referencia del estandar JAX-RS, Jersey, viene con facilidades para utilizar diversas librerias JSON como conversores y codificadores de las entradas y salidas del API REST.

Sin embargo yo quería usar una que no está incluida entra las habituales de Jersey. Por suerte JAX-RS proporciona unos mecanismos de extensión sencillos.

Esta es una clase de ejemplo, un proveedor, para utilizar Gson en Jersey.

Como se puede ver en el método getGson es donde configuramos la instancia de Gson así que ahí podriamos incluir los diferentes adaptadores especiales de objetos, como he hecho yo con un par de clases de la nueva libreria java.time de Java 8.

/**
 * Proveïdor Jersey per fer servir Gson com a parser JSON.
 */
@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class JerseyServerGson  
        implements MessageBodyWriter<Object>, MessageBodyReader<Object>
{
  @Override
  public boolean isReadable(Class<?> type,
                            Type genericType,
                            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 IOException, WebApplicationException
  {
    try ( InputStreamReader input = 
                  new InputStreamReader(entityStream, "UTF-8") ) {
      Gson gson = getGson();
      return gson.fromJson(input, genericType);
    }
  }

  @NotNull
  private Gson getGson() {
    return new GsonBuilder()
            .registerTypeAdapter(LocalDateTime.class,
                                 new AdapterLocalDateTime().nullSafe())
            .registerTypeAdapter(LocalDate.class,
                                 new AdapterLocalDate().nullSafe())
            .setPrettyPrinting()
            .serializeNulls()
            .create();
  }

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

  @Override
  public long getSize(Object o,
                      Class<?> type,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mediaType)
  {
    // Deprecated and ignored in Jersey 2
    return -1;
  }

  @Override
  public void writeTo(Object o,
                      Class<?> type,
                      Type genericType,
                      Annotation[] annotations,
                      MediaType mediaType,
                      MultivaluedMap<String, Object> httpHeaders,
                      OutputStream entityStream)
          throws IOException, WebApplicationException
  {
    try ( OutputStreamWriter writer = 
                  new OutputStreamWriter(entityStream, "UTF-8") ) {
      getGson().toJson(o, genericType, writer);
    }
  }
}