Using Volley and Gson: Parse item and items list
Asked Answered
C

3

6

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);
    }

}
Cantonment answered 6/10, 2013 at 20:54 Comment(3)
Class Class actually implements Type interface so you don't really a constructor that takes Class as an argument.Imhoff
Humm... i was almost sure that I had gotten some error passing a Type to gson.fromJson when I wanted just an item to get parsed. Anyway, I've just tried it using Type and it worked so maybe I just need to use Type as you say. Post it as an answer and I'll accept it :)Cantonment
Take a look at this article that explains exactly that. goo.gl/nl2DfNSolitary
P
5

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.

Paraphrase answered 10/12, 2014 at 0:34 Comment(1)
Thanks. Used your method parseNetworkResponse(...) for parameter type. Working fine.Tractarianism
P
3

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));
Patrizio answered 27/3, 2014 at 12:9 Comment(3)
do you need to add a new / different constructor to your gsonRequest Class?Tourcoing
Yes, and it works for me. Could you be more specific? What kind of error do you get?Patrizio
Ran into the same issue while trying to use TypeToken. Update your GsonRequest class to accept Type in the constructor instead of Class. Also change the way you parse the JSON in parseNetworkResponse to the way Tooroop does in his answer.Santo
R
0

I used the JsonObject request of the Volley and used the Response.ToString() to parse the Json String to Class through Gson.

Gson gson = new Gson();
ClassName obj = gson.fromJson(response.ToString(),ClassName.class);

Now you have obj with all data.

Raina answered 2/10, 2014 at 13:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.