Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Volley and Gson: Parse item and items list

One thing I've never liked about Gson is the fact that you have to pass a Class object or a TypeToken based on if you're getting an item or a list of items. Now, when trying to use Volley with Gson this problem persists and I'm trying to make a GsonRequest class that can be used for both things.

My solution is quite ugly, two different constructors: one getting a Class<T> parameter and another one getting a Type parameters. Then, in the parseNetworkResponse, gson.fromJson is called with either one of the fields, keeping in mind that one has to be null.

Any idea of how to implement this in a better way? (I don't like having a GsonRequest and a GsonCollectionRequest almost-equal classes)

My code, here:

public class GsonRequest<T> extends Request<T> {

    private final Gson gson;
    private final Class<T> clazz;
    private final Type type;
    private final Listener<T> listener;
    private final Map<String, String> headers;
    private final Map<String, String> params;

    public GsonRequest(int method, String url, Gson gson, Class<T> clazz, Map<String, String> headers, Map<String, String> params, Listener<T> listener, ErrorListener errorListener) {
        super(method, url, errorListener);
        this.gson = gson;
        this.clazz = clazz;
        this.type = null;
        this.listener = listener;
        this.headers = headers;
        this.params = params;
    }

    public GsonRequest(int method, String url, Gson gson, Type type, Map<String, String> headers, Map<String, String> params, Listener<T> listener, ErrorListener errorListener) {
        super(method, url, errorListener);
        this.gson = gson;
        this.clazz = null;
        this.type = type;
        this.listener = listener;
        this.headers = headers;
        this.params = params;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return this.headers != null ? this.headers : super.getHeaders();
    }

    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        return this.params != null ? this.params : super.getParams();
    }

    @Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
        try {

            if (this.clazz != null) {
                return Response.success(
                        this.gson.fromJson(new String(response.data, HttpHeaderParser.parseCharset(response.headers)), this.clazz),
                        HttpHeaderParser.parseCacheHeaders(response));
            } else {
                return (Response<T>) Response.success(
                        this.gson.fromJson(new String(response.data, HttpHeaderParser.parseCharset(response.headers)), this.type),
                        HttpHeaderParser.parseCacheHeaders(response));
            }

        } catch (JsonSyntaxException e) {
            e.printStackTrace();
            return Response.error(new ParseError(e));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(T response) {
        this.listener.onResponse(response);
    }

}
like image 987
luixal Avatar asked Oct 06 '13 20:10

luixal


2 Answers

I used the following method to parse a JSON list. As first don't send a Class in the constructor, instead pass the Type class from the reflect package.

My class looks like this:

public class DownloadRequest<T> extends Request<T> {

private final Gson gson = new Gson();
private final Type type;
private final Map<String, String> params;
private final Response.Listener<T> listener;

public DownloadRequest(int method, String url, Map<String, String> params, Type type, Response.Listener<T> listener, Response.ErrorListener errorListener) {
    super(method, url, errorListener);
    this.type = type;
    this.params = params;
    this.listener = listener;
}

@Override
protected Response<T> parseNetworkResponse(NetworkResponse networkResponse) {

    try {
        String json = new String(networkResponse.data, HttpHeaderParser.parseCharset(networkResponse.headers));
        T parseObject = gson.fromJson(json, type);
        return Response.success(parseObject,HttpHeaderParser.parseCacheHeaders(networkResponse));
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    return null;
}

@Override
protected void deliverResponse(T t) {
    listener.onResponse(t);
}

}

The line T parseObject = gson.fromJson(json, type); is important to set before you call the Request.success method.

like image 115
Tooroop Avatar answered Oct 14 '22 06:10

Tooroop


You can create a new GsonRequest using TypeToken as Type parameter.

Use generic GsonRequest like this GsonRequest.

Create a simple Request for a Gson class...

new GsonRequest<MyClass>(Request.Method.GET, uriBuilder.build().toString(),
                    MyClass.class, null, mResponseListener, mReponseErrorListener));

or create a type for an ArrayList...

Type type = new TypeToken<ArrayList<MyClass>>() {}.getType();
new GsonRequest<ArrayList<MyClass>>(Request.Method.GET, uriBuilder.build().toString(),
                    type, null, mResponseListListener, mReponseErrorListener));
like image 43
jgonza73 Avatar answered Oct 14 '22 08:10

jgonza73