Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Response handling with Volley

I am using Volleyin my project for handling network requests. Here is a sample JSON my server returns

JSON Object Response

{"code":"success", "data":{"some data"}}

JSON Array Response

{"code":"success", "data":["some data"]}

When some validation error or any other error occurs, server returns following response:

{"code":"failed", "error":"Access denied"}

The problem is with parsing data. when request is successful, in onResponse of ResponseListener, I simply get the content of data key. Where as, I was expecting the result same as what I posted above. I am not getting why Volley is returning only content of data and not complete JSON. I had used Volley earlier also. But never faced such type of problem.

Parsing Code:

private void getOnboardingCategories() {
    Response.Listener<JSONArray> responseListener = new Response.Listener<JSONArray>() {

        @Override
        public void onResponse(JSONArray response) {
            Log.d(LOG_TAG, "CATEGORY RESPONSE: " + response.toString());
            if (response != null) {
                int dataLength = response.length();
                for (int i = 0; i < dataLength; i++) {
                    JSONObject jObject = response.optJSONObject(i);
                    if (jObject != null) {
                        CategoryType2 categoryType2 = new CategoryType2();
                        categoryType2.set_id(jObject.optString("_id"));
                        categoryType2.setName(jObject.optString("name"));
                        categoryType2.setApp_icon_data(jObject.optString("thumbnail_data"));
                        categories.add(categoryType2);
                    }
                }
            }
            if (isVisible())
                sellAdapter.notifyDataSetChanged();
        }
    };

    Response.ErrorListener errorListener = new Response.ErrorListener() {

        @Override
        public void onErrorResponse(VolleyError error) {
            error.printStackTrace();
            Util.errorHandler(error, ctx);
        }
    };

    JsonArrayRequest jsonObjectRequest = new JsonArrayRequest(Method.GET, url,
            null, responseListener, errorListener);
    MyApplication.getInstance().addToRequestQueue(jsonObjectRequest, "onboarding");
}

Response on Success:

{
   code: "success",
   data: [
             {
                 _id: "55c06b05a3e0041a73cea744",
                 name: "Test Category 1",
                 thumbnail_data: "",
             },
             {
                 _id: "55c06b16a3e0046108cea744",
                 name: "Test Category 2",
                 thumbnail_data: "",
             }
         ]
}

In onResponse of ResponseListener, I get this data:

[
    {
        _id: "55c06b05a3e0041a73cea744",
        name: "Test Category 1",
        thumbnail_data: "",
    },
    {
        _id: "55c06b16a3e0046108cea744",
        name: "Test Category 2",
        thumbnail_data: "",
    }
]

When error occurs, server returns this response:

{"code":"failed", "error":"error_msg"}

Due to this, Volley throws ParseException as it expects JSONArray. I need to show the error message to the user. Earlier, I was using AsyncTask and I handled the error there. But, with Volley I am facing difficulty. I looked into VolleyError, but didn't got any clue.

Update 1

private void getOnboardingCategories() {
    showSpinnerDialog(true);
    Response.Listener<JSONObject> responseListener = new Response.Listener<JSONObject>() {

        @Override
        public void onResponse(JSONObject response) {
            Log.d(LOG_TAG, "CATEGORY RESPONSE: " + response.toString());
            hideSpinnerDialog();
            String code = response.optString("code");
            if (code.equals("success")) {
                if (response != null) {
                    JSONArray dataArray = response.optJSONArray("data");
                    int dataLength = dataArray.length();
                    for (int i = 0; i < dataLength; i++) {
                        JSONObject jObject = dataArray.optJSONObject(i);
                        if (jObject != null) {
                            CategoryType2 categoryType2 = new CategoryType2();
                            categoryType2.set_id(jObject.optString("_id"));
                            categoryType2.setName(jObject.optString("name"));
                            categoryType2.setApp_icon_data(jObject.optString("app_icon_data"));
                            categories.add(categoryType2);
                        }
                    }
                }
            }
            if (isVisible())
                sellAdapter.notifyDataSetChanged();
        }
    };

    Response.ErrorListener errorListener = new Response.ErrorListener() {

        @Override
        public void onErrorResponse(VolleyError error) {
            error.printStackTrace();
            Util.errorHandler(error, ctx);
        }
    };

    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Method.GET, url,
            null, responseListener, errorListener);
    MyApplication.getInstance().addToRequestQueue(jsonObjectRequest, "onboarding");
}

Update This issue was not about Volley. There was issue on the server end wrt gzip compression. I am going to vote for closing this question.

like image 857
Nitish Avatar asked Aug 31 '15 04:08

Nitish


People also ask

How do I get volley response error?

If you want to view the data in the case of an error code in response, you can use something like this: @Override public void onErrorResponse(VolleyError error) { String body; //get status code here String statusCode = String. valueOf(error. networkResponse.

Which is better retrofit or volley?

Volley can provide fine grain control over caching strategy, but its harder to configure caching than Retrofit. Retrofit relies on OkHttp, which relies on Okio which effectively makes this library huge compared to a basic configuration of Volley. If size is an issue, Volley is a better bet.

What is a volley error?

Subclasses of VolleyError in com.android.volley. class. AuthFailureError. Error indicating that there was an authentication failure when performing a Request. class.


6 Answers

UPDATE RESULT SCREENSHOTS:

Success case: JSONArray

[
    {
        "_id": "55c06b05a3e0041a73cea744",
        "name": "Category 1",
        "thumbnail_data": ""
    },
    {
        "_id": "55c06b16a3e0046108cea744",
        "name": "Category 2",
        "thumbnail_data": ""
    }
]

enter image description here

Error case: JSONObject

{
    "code": "failed",
    "error": "error_msg"
}

enter image description here

In my code below, pay attention to parseNetworkResponse.


The following is my updated answer, I have tested for both responses you provided:

        RequestQueue queue = Volley.newRequestQueue(mContext);
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(0, url, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    if (!response.isNull("success")) {
                        JSONArray jsonArray = response.getJSONArray("success");
                        Toast.makeText(mContext, "onResponse:\n\n" + jsonArray.toString(5), Toast.LENGTH_SHORT).show();
                        if (mTextView != null) {
                            mTextView.setText(jsonArray.toString(5));
                        }
                    } else {
                        String codeValue = response.getString("code");
                        if ("failed".equals(codeValue)) {
                            String errorMessage = response.getString("error");
                            Toast.makeText(mContext, "Error Message:\n\n" + errorMessage, Toast.LENGTH_SHORT).show();
                            if (mTextView != null) {
                                mTextView.setText("Error Message:\n\n" + errorMessage);
                            }
                        }
                    }                        
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(mContext, "onErrorResponse:\n\n" + error.toString(), Toast.LENGTH_SHORT).show();
            }
        }) {
            @Override
            protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
                try {
                    String jsonString = new String(response.data,
                            HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
                    // Check if it is JSONObject or JSONArray
                    Object json = new JSONTokener(jsonString).nextValue();
                    JSONObject jsonObject = new JSONObject();
                    if (json instanceof JSONObject) {
                        jsonObject = (JSONObject) json;
                    } else if (json instanceof JSONArray) {
                        jsonObject.put("success", json);
                    } else {
                        String message = "{\"error\":\"Unknown Error\",\"code\":\"failed\"}";
                        jsonObject = new JSONObject(message);
                    }
                    return Response.success(jsonObject,
                            HttpHeaderParser.parseCacheHeaders(response));
                } catch (UnsupportedEncodingException e) {
                    return Response.error(new ParseError(e));
                } catch (JSONException e) {
                    return Response.error(new ParseError(e));
                }
            }
        };

        queue.add(jsonObjectRequest);

Hope this helps!

like image 112
BNK Avatar answered Nov 13 '22 10:11

BNK


An efficient method to handle this kinda situation can be achieved through parsing JSON values using GSON and assign the key values using POJO class.

Example:

Add error scenario in both the cases like handling JSONArray or JSONObject. Please find the samples of your required POJO for your test data as follows.

Sample 1

public class JSONArrayPojo
{
    private ArrayList<String> data;

    private String code;

    private String error;

   public String getError() {
      return this.error;
   }

   public void setError(String value) {
      this.error = value;
   }

    public ArrayList<String> getData ()
    {
        return data;
    }

    public void setData (ArrayList<String> data)
    {
        this.data = data;
    }

    public String getCode ()
    {
        return code;
    }

    public void setCode (String code)
    {
        this.code = code;
    }
}

Sample 2

public class JSONObjectPojo
{
    private String data;

    private String code;

    private String error;

   public String getError() {
      return this.error;
   }

   public void setError(String value) {
      this.error = value;
   }

    public String getData ()
    {
        return data;
    }

    public void setData (String data)
    {
        this.data = data;
    }

    public String getCode ()
    {
        return code;
    }

    public void setCode (String code)
    {
        this.code = code;
    }

}

Generating GSON from your response and handling out the both positive(success) and negativ(error) scenario as follows:

@Override
        public void onResponse(JSONArray response) {
            Log.d(LOG_TAG, "CCMP CATEGORY RESPONSE: " + response.toString());
            if (response != null) {

//converting JSON response into GSON Format

                JSONArraryPojo jsonArray = null;
                GsonBuilder gsonBuilder = new GsonBuilder();
                gsonBuilder.serializeNulls();
                Gson gson = gsonBuilder.create();
                jsonArray = gson.fromJson(response.toString(), JSONArraryPojo.class);

                if(jsonArray.getCode().equals("success")){

                     //process your steps if the value is success
                     Toast.makeText(this, jsonArray.getCode(), Toast.LENGTH_SHORT).show();

                }else {
                      //displaying toast when error occurs
                      Toast.makeText(this, jsonArray.getError(),    Toast.LENGTH_SHORT).show();

                }
            }

        }
    };

Reference links to parse into GSON from JSON

http://kylewbanks.com/blog/Tutorial-Android-Parsing-JSON-with-GSON

http://examples.javacodegeeks.com/core-java/json/json-parsing-with-gson/

http://www.javacodegeeks.com/2011/01/android-json-parsing-gson-tutorial.html

http://www.mysamplecode.com/2013/07/android-json-stream-data-parsing.html

http://blog.nkdroidsolutions.com/

Note: To make use of GSON library in android.

Add following lines in gradle:

compile 'org.immutables:gson:2.1.0.alpha' 
like image 43
Chandru Avatar answered Nov 13 '22 11:11

Chandru


In case of error your server should return error json with http status code 4xx. That is how you design Restful APIs. In current situation, your API is always returning 2xx which corresponds to successful API result. If your API sends correct http response code, in this case 401 (unauthorized) or 403 (forbidden) refer here then your ErrorListener will be called by Volley. You don't have to write parsing logic for error response in ResponseListener. Here is a good resource for understanding rest api http status codes.

like image 39
JavaGhost Avatar answered Nov 13 '22 10:11

JavaGhost


But, when error occurs, I get Parse exception, when making request for JSONArray

Use JSONObject. has() and JSONObject. isNull() to check which key is present in json response before parsing json.

For Example:

JSONObject jsonObject=new JSONObject(<server_response_string>);
  if(jsonObject.has("data") && !jsonObject.isNull("data"))
  {
    // get data JSONArray from response
  }else{
     // get message using error key
   }
like image 21
ρяσѕρєя K Avatar answered Nov 13 '22 10:11

ρяσѕρєя K


You can check the value of code key as it will always be available to you whether the response would be a failure or success. Below is a small snippet:

JSONObject jObj = new JSONObject(your_response_string);
if(jObj.getString("code").equalsIgnoreCase("failed"))
{
    //write code for failure....
}
else{
       //write code for success......
}

Note: A more modular way to do this is to make a model class and set your values in it. This way you will get all your values in a single java object.

like image 31
Kaveesh Kanwal Avatar answered Nov 13 '22 09:11

Kaveesh Kanwal


Maybe you can try this:
Use the fastjson library to convert your json string to your java object in Response.Listener like this: Pojo pojo = JSON.parseObject(response.toString(), Pojo.class);

And your Pojo may like this:

public class Pojo {
private String code;
private String error;
private List<Date> data;

public String getCode() {
    return code;
}

public void setCode(String code) {
    this.code = code;
}

public String getError() {
    return error;
}

public void setError(String error) {
    this.error = error;
}

public List<Date> getData() {
    return data;
}

public void setData(List<Date> data) {
    this.data = data;
}

public class Data {
    public String _id;
    public String name;
    public String thumbnail_data;

    public String get_id() {
        return _id;
    }

    public void set_id(String _id) {
        this._id = _id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getThumbnail_data() {
        return thumbnail_data;
    }

    public void setThumbnail_data(String thumbnail_data) {
        this.thumbnail_data = thumbnail_data;
    }
}
}

then what you need to do is to check if your pojo has value such as error,if error is not null ,you need to handle it .
The fastjson library has help you to avoid crash while converting.

you can find fastjson here

like image 20
uestcfei Avatar answered Nov 13 '22 11:11

uestcfei