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);
}
}
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.
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));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With