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));
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.
Related
I have created an abstract request to make it easier to send requests.
abstract class MyRequest<T> extends Request<T> {
private final static String API_KEY = "";
private final static String API_STRING = "?api_key=";
private final Class<T> objectType;
private final Response.Listener<T> listener;
public MyRequest(String url, Class<T> objectType, Response.Listener<T> listener, #Nullable Response.ErrorListener errorListener) {
super(Method.GET, url + API_STRING + API_KEY, errorListener);
this.objectType = objectType;
this.listener = listener;
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
return Response.success(Mapper.getMapper().readValue(new String(response.data, HttpHeaderParser.parseCharset(response.headers)), objectType),
HttpHeaderParser.parseCacheHeaders(response));
} catch (IOException e) {
return Response.error(new ParseError(e));
}
}
#Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}
}
This code is used to make a custom request like this:
public class MasteryRequest extends MyRequest<Summoner> {
private static final String URL = "https://euw1.api.riotgames.com/lol/champion-mastery/v4/champion-masteries/by-summoner/";
public MasteryRequest(String summonerId, Response.Listener<Summoner> listener, #Nullable Response.ErrorListener errorListener) {
super(URL + summonerId, Summoner.class, listener, errorListener);
}
}
Now my problem is the API returns an array of objects and jackson is unable to convert that into my Summoner object. The summoner object looks like this:
public class Summoner {
private List<MasteryChampion> masteryChampions;
public List<MasteryChampion> getMasteryChampions() {
return masteryChampions;
}
}
It seems like jackson doesn't know how to put the return value into the array of summoner. Is there any annotation that I am missing so jackson knows how to put the array into summoner?
Response body example:
[
{
"championId": 92,
"championLevel": 7,
"championPoints": 1029885,
"lastPlayTime": 1653428885000,
"championPointsSinceLastLevel": 1008285,
"championPointsUntilNextLevel": 0,
"chestGranted": true,
"tokensEarned": 0,
"summonerId": "03tFEbQfU5-pL1y8xR0XBckv6bJx1M6OtfVe8WKQtc-_AFc"
},
{
"championId": 114,
"championLevel": 7,
"championPoints": 274271,
"lastPlayTime": 1653418873000,
"championPointsSinceLastLevel": 252671,
"championPointsUntilNextLevel": 0,
"chestGranted": true,
"tokensEarned": 0,
"summonerId": "03tFEbQfU5-pL1y8xR0XBckv6bJx1M6OtfVe8WKQtc-_AFc"
}
]
How to send data in x-www-form-urlencoded in android to pass delete request?
#Override
public byte[] getBody() {
Map<String, String> params = new HashMap<>();
params.put("is_admin","1");
params.put("client_dbname",sessionManager.clientdbname());
params.put("user_id" ,"1");
//yeah, I copied this from the base method.
if (params !=null)
{
try {
para = params.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return para;
}
This because Volley doesn't send the Body for DELETE by default. Only for POST, PUT and PATCH.
Use this third party for Delete Request
https://github.com/ngocchung/DeleteRequest
Try this class for Delete Request:
public class StringJSONBodyReqest extends StringRequest {
private static final String TAG = StringJSONBodyReqest.class.getName();
private final String mContent;
public StringJSONBodyReqest(int method, String url, String content, Response.Listener<String> listener, Response.ErrorListener errorListener) {
super(method, url, listener, errorListener);
mContent = content;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<String, String>();
headers.put("api-version", "1");
return headers;
}
#Override
public byte[] getBody() throws AuthFailureError {
byte[] body = new byte[0];
try {
body = mContent.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Unable to gets bytes from JSON", e.fillInStackTrace());
}
return body;
}
#Override
public String getBodyContentType() {
return "application/json";
}
}
i have an json object like this and i am getting this response in my Fragment.
json
{
"data":{
"categories":[
{
"id":"d5c4eedf-093e-422f-8335-6c6376ca3ccb",
"schedule_m_id":1,
"title_en":"Bakery Products",
"title_fr":"Produits de boulangerie",
"subtitle_en":"Bread, Cakes, Cookies, Crackers, Pies",
"subtitle_fr":"Pain, gateaux, biscuits, craquelins, tartes",
"created_at":"2015-03-04 15:39:44",
"updated_at":"2015-03-04 15:39:44"
},
{
"id":"6d1d4945-9910-40ae-82a8-3fe4137c24c2",
"schedule_m_id":2,
"title_en":"Beverages",
"title_fr":"Boissons",
"subtitle_en":"Soft Drinks, Coffee, Tea, Cocoa",
"subtitle_fr":"Boissons gazeuses, café, thé, cacao",
"created_at":"2015-03-04 15:39:44",
"updated_at":"2015-03-04 15:39:44"
}
]
},
"result":"success"
}
and my categories class is like this:
public class Categories {
private int id;
private String title_en;
private String title_fr;
private int schedule_m_id;
private String subtitle_en;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle_en() {
return title_en;
}
public void setTitle_en(String title_en) {
this.title_en = title_en;
}
public String getTitle_fr() {
return title_fr;
}
public void setTitle_fr(String title_fr) {
this.title_fr = title_fr;
}
public int getSchedule_m_id() {
return schedule_m_id;
}
public void setSchedule_m_id(int schedule_m_id) {
this.schedule_m_id = schedule_m_id;
}
public String getSubtitle_en() {
return subtitle_en;
}
public void setSubtitle_en(String subtitle_en) {
this.subtitle_en = subtitle_en;
}
}
In my fragment how can i parse this json object. i need to make an ArrayList which type is "Categories". i need this Categories object List to make an custom adapter. Can anybode help me.
JSONObject jsonObject = (JSONObject) response;
JSONObject dataProject = jsonObject.getJSONObject("data");
JSONArray products = dataProject.getJSONArray("categories");
Gson gson = new Gson();
Categories categories = new Categories();
ArrayList<Categories> items = new ArrayList<Categories>();
int productCount = products.length();
for (int i = 0; i < productCount; i++) {
categories = gson.fromJson(products.get(i), Categories.class);
items.add(categories);
}
```
I posting a class working with gson volley May be Helpful for you....
Step1. For Parsing your json data use "www.jsonschema2pojo.org/" and generate pojo classes. copy classes in your project with same name.
Step2. Just create a GsonRequest Class as follows (taken from https://developer.android.com/training/volley/request-custom.html)
public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Listener<T> listener;
/**
* Make a GET request and return a parsed object from JSON.
*
* #param url URL of the request to make
* #param clazz Relevant class object, for Gson's reflection
* #param headers Map of request headers
*/
public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
Listener<T> listener, ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
#Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(
response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(
gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
Step3.Now in your main Activity just use this "GsonRequest" class like that:
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
GsonRequest<MyPojoClass> gsonRequest = new GsonRequest<MyPojoClass>(
Request.Method.GET,
apiurl,
MyPojoClass.class,
mySuccessListener(),
myErrorListener());
//Add below these code lines for "Retry" data fetching from api
gsonRequest.setRetryPolicy(new DefaultRetryPolicy(
5000,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
mRequestQueue.add(gsonRequest);
}
private Response.Listener<MyPojoClass> mySuccessListener() {
return new Response.Listener<CustomRequest>() {
#Override
public void onResponse(MyPojoClass pRequest) {
//do something
}
};
}
private Response.ErrorListener myErrorListener() {
return new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
System.out.println(volleyError.getMessage().toString());
}
};
}
I am using Volley to fetch data from a REST service. So there are two methods , One is POST which creates an object on server and other is PUT which basically updates the same object.I use the same code for making these requests.I just change the Method which is passed in Volley.The app works fine through POST request with the correct data sent in the body, but it encounters a problem with PUT request where it randomly sends no data in the body.
I shifted to a custom Request object as suggested by Google but it is still not working.
int method = Request.Method.POST;
String objID = sharedPreferences.getString("objID", null);
String Url = "/api_url";
if (objID != null)
{
Url += objID + "/";
method = Request.Method.PUT;
}
try
{
JSONObject objJSON = new JSONObject();
objJSON.put("name", "new_name");
}
catch (JSONException e)
{
// Handle Execption
}
GsonRequest request = new GsonRequest<CustomObj>(method, Url, CustomObj.class,null, null, objJSON.toString(),
new Response.Listener<CustomObj>()
{
#Override
public void onResponse(CartObj cartObj)
{
// Handling Logic
}
}
,
new Response.ErrorListener()
{
#Override
public void onErrorResponse(VolleyError volleyError)
{
// Handle Error
}
});
VolleySingleton.getInstance(this).addToRequestQueue(request);
GsonRequest is my Custom Class Extending Request Object
public class GsonRequest<T> extends Request<T>
{
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Listener<T> listener;
private final Map<String, String> params;
private final String body;
private ErrorListener mErrorListener;
/**
* Make a GET request and return a parsed object from JSON.
*
* #param url URL of the request to make
* #param clazz Relevant class object, for Gson's reflection
* #param headers Map of request headers
*/
public GsonRequest(int method, String url, Class<T> clazz, Map<String, String> headers, Map<String, String> params, String body, Listener<T> listener, ErrorListener errorListener)
{
super(method, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
this.params = params;
this.body = body;
this.mErrorListener = errorListener;
}
public GsonRequest(int method, String url, Class<T> clazz, Map<String, String> headers, Map<String, String> params, Listener<T> listener, ErrorListener errorListener)
{
this(method, url, clazz, headers, params, null, listener, errorListener);
}
public GsonRequest(int method, String url, Class<T> clazz, Map<String, String> headers, Listener<T> listener, ErrorListener errorListener)
{
this(method, url, clazz, headers, null, null, listener, errorListener);
}
#Override
public byte[] getBody() throws AuthFailureError
{
return body != null ? body.getBytes() : super.getBody();
}
#Override
protected Map<String, String> getParams() throws AuthFailureError
{
return params != null ? params : super.getParams();
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError
{
return headers != null ? headers : super.getHeaders();
}
#Override
protected void deliverResponse(T response)
{
listener.onResponse(response);
}
#Override
public void deliverError(VolleyError error)
{
mErrorListener.onErrorResponse(error);
}
#SuppressWarnings("unchecked")
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response)
{
try
{
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(gson.fromJson(json, clazz), HttpHeaderParser.parseCacheHeaders(response));
}
catch (UnsupportedEncodingException e)
{
return Response.error(new ParseError(e));
}
catch (JsonSyntaxException e)
{
return Response.error(new ParseError(e));
}
}
}
I am using GSON to parse my Data , and CustomObj is a Mapping Object for volley.
First of all all make sure the url you're building (where you check if objID is not null) is correct.
Secondly, you can try to call getBytes() method with a charset.
In the request override getBody() method:
#Override
public byte[] getBody() throws AuthFailureError {
try {
return body != null ? body.getBytes(getParamsEncoding()) : super.getBody();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return super.getBody();
}
Hope this will help.
Edit:
You can also try to override getBodyContentType() method:
#Override
public String getBodyContentType() {
return "application/json; charset=" + getParamsEncoding();
}
I am trying to make a Gson request using android volley. Currently it it working correctly and I am very happy. However why I try and get a List<> or a collection of objects my code no longer works.
Current code:
public class ReviewModel
{
public long Id;
public Strring Description;
}
here is how I use my gson class:
GsonRequest<ReviewModel> jsObjRequest = new GsonRequest<ReviewModel>(Request.Method.GET,
url, ReviewModel.class, new Response.Listener<ReviewModel>() {
#Override
public void onResponse(ReviewModel response) {
ReviewsHandleOkResponse(response);
}
}, new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
ReviewsHandleErrorResponse(error);
}
});
Network.getInstance(getActivity()).addToRequestQueue(jsObjRequest);
}
here is my Volley GSON Request Class:
public class GsonRequest<T> extends Request<T> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Map<String, String> params;
private final Listener<T> listener;
/**
* Make a GET request and return a parsed object from JSON.
*
* #param url
* URL of the request to make
* #param clazz
* Relevant class object, for Gson's reflection
* #param headers
* Map of request headers
*/
public GsonRequest(int method, String url, Class<T> clazz,
Map<String, String> headers, Map<String, String> params,
Listener<T> listener, ErrorListener errorListener) {
super(method, Network.getFullUrl(url), errorListener);
this.clazz = clazz;
this.headers = headers;
this.params = params;
this.listener = listener;
}
/**
* Recieves header
*
* #param method
* #param url
* #param clazz
* #param params
* #param listener
* #param errorListener
*/
public GsonRequest(int method, String url, Class<T> clazz,
Map<String, String> params, Listener<T> listener,
ErrorListener errorListener) {
super(method, Network.getFullUrl(url), errorListener);
this.clazz = clazz;
this.headers = new HashMap<String, String>();
this.params = params;
this.listener = listener;
}
/**
* No params or headers
*
* #param method
* #param url
* #param clazz
* #param listener
* #param errorListener
*/
public GsonRequest(int method, String url, Class<T> clazz,
Listener<T> listener, ErrorListener errorListener) {
super(method, Network.getFullUrl(url), errorListener);
this.clazz = clazz;
this.headers = new HashMap<String, String>();
this.params = new HashMap<String, String>();
this.listener = listener;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
#Override
public Map<String, String> getParams() throws AuthFailureError {
return params != null ? params : super.getParams();
}
#Override
protected void deliverResponse(T response) {
listener.onResponse(response);
}
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(gson.fromJson(json, clazz),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
}
however when I rey and parse it to a List<ReviewModel> I get compile errors.
From what I have researched, I need to do some thing like :
Type collectionType = new TypeToken<ArrayList<ReviewModel>>(){}.getType();
// api/v1/reviews/products/{productId}/{pageNumber}/{pageSize}
String url = "api/v1/reviews/products/" + productId + "/" + currentPage + "/" + pageSize;
GsonRequest<ArrayList<ReviewModel>> jsObjRequest = new GsonRequest<ArrayList<ReviewModel>>(Request.Method.GET,
url, (Class<ArrayList<ReviewModel>>) collectionType, new Response.Listener<ArrayList<ReviewModel>>() {
#Override
public void onResponse(ArrayList<ReviewModel> response) {
ReviewsHandleOkResponse(response);
}
}, new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
ReviewsHandleErrorResponse(error);
}
});
Network.getInstance(getActivity()).addToRequestQueue(jsObjRequest);
}
but this gives me the following error:
10-28 21:12:59.940: E/AndroidRuntime(28564):
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.menu/com.menu.activities.ProductViewActivity}:
java.lang.ClassCastException:
com.google.gson.internal.$Gson$Types$ParameterizedTypeImpl cannot be
cast to java.lang.Class
Do i need to modify my GsonRequest Class, and add another constructor which doesn't take a type class?
Use YourClass[] for List of objects in Volley request. It has solved my issue. The following code should help you:
GsonRequest<ReviewModel[]> jsObjRequest = new GsonRequest<ReviewModel[]>(Request.Method.GET,
url, ReviewModel[].class, new Response.Listener<ReviewModel[]>() {
#Override
public void onResponse(ReviewModel[] response) {
ReviewsHandleOkResponse(response);
}
}, new ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
ReviewsHandleErrorResponse(error);
}
});
Network.getInstance(getActivity()).addToRequestQueue(jsObjRequest);
}
I recently needed to implement a solution myself so here you go.
Custom GsonArrayRequest.
public class GsonArrayRequest<T> extends Request<ArrayList<T>> {
private final Gson gson = new Gson();
private final Class<T> clazz;
private final Map<String, String> headers;
private final Response.Listener<ArrayList<T>> listener;
/**
* Make a GET request and return a parsed object from JSON.
* #param clazz Relevant class object, for Gson's reflection
* #param url URL of the request to make
* #param headers Map of request headers
*/
public GsonArrayRequest(String url, Class<T> clazz, Map<String, String> headers,
Response.Listener<ArrayList<T>> listener, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.clazz = clazz;
this.headers = headers;
this.listener = listener;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return headers != null ? headers : super.getHeaders();
}
#Override
protected Response<ArrayList<T>> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(
response.data,
HttpHeaderParser.parseCharset(response.headers));
Type listType = com.google.gson.internal.$Gson$Types.newParameterizedTypeWithOwner(null, ArrayList.class, clazz);
ArrayList<T> tList = gson.fromJson(json, listType);
return Response.success(
tList,
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JsonSyntaxException e) {
return Response.error(new ParseError(e));
}
}
#Override
protected void deliverResponse(ArrayList<T> response) {
listener.onResponse(response);
}
}
You can use it like this:
GsonArrayRequest<StoreWorkout> gsonArrayRequest = new GsonArrayRequest<>(
url, AngryPugs.class, null, new Response.Listener<ArrayList<AngryPugs>>() {
#Override
public void onResponse(ArrayList<AngryPugs> response) {
// your list here
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
After adding the request
VolleySingleton.getInstance(this).addToRequestQueue(gsonArrayRequest);