2013年1月10日 星期四

Customize JAX-RS Provider (JSR311)

以下是使用google-gson處理RESTful JSON message的provider,其中日期的部分使用ISO8601格式。
com.gss.gmo.cao.restclient.provider.GsonMessageBodyProvider
package com.gss.gmo.cao.restclient.provider;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Date;
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;
import com.gss.gmo.cao.gson.ISODateTimeAdapter;

/**
 * MessageBodyWriter and MessageBodyReader for MediaType.APPLICATION_JSON.
 *
 * @author linus_chien
 *
 */
@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class GsonMessageBodyProvider implements MessageBodyWriter<Object>, MessageBodyReader<Object> {

    /**
     * Gson instance.
     */
    private final Gson gson;

    /**
     * Init Gson object.
     */
    public GsonMessageBodyProvider() {
        gson = new GsonBuilder().registerTypeAdapter(Date.class, new ISODateTimeAdapter()).create();
    }

    @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 {
        return gson.fromJson(new InputStreamReader(entityStream, "UTF-8"), genericType);
    }

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

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

    @Override
    public void writeTo(Object obj, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
            OutputStream entityStream) throws IOException, WebApplicationException {
        entityStream.write(gson.toJson(obj, genericType).getBytes("UTF-8"));
    }
}
com.gss.gmo.cao.gson.ISODateTimeAdapter
package com.gss.gmo.cao.gson;

import org.joda.time.format.ISODateTimeFormat;

/**
 * Format sample: 2012-12-05T10:55:41.063+08:00
 *
 * @author linus_chien
 *
 */
public class ISODateTimeAdapter extends AbstractDateTimeAdapter {

    public ISODateTimeAdapter() {
        super(ISODateTimeFormat.dateTime());
    }
}
com.gss.gmo.cao.gson.AbstractDateTimeAdapter
package com.gss.gmo.cao.gson;

import java.lang.reflect.Type;
import java.util.Date;
import org.joda.time.format.DateTimeFormatter;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

public abstract class AbstractDateTimeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {

    private DateTimeFormatter formatter;

    protected AbstractDateTimeAdapter(DateTimeFormatter formatter) {
        this.formatter = formatter;
    }

    @Override
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        return formatter.parseDateTime(json.getAsString()).toDate();
    }

    @Override
    public JsonElement serialize(Date date, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(formatter.print(date.getTime()));
    }
}