JSONException trying to extend JsonObjectRequest - android

I am running into a syntax problem but I can't figure out what is wrong. It seems like this thread has the answer but it does work for me.
JSONException: Value of type java.lang.String cannot be converted to JSONObject
I am using Volley I need to extend the JsonObjectRequest so that I can access the http headers in the response. Here is my "CustomRequest"
public class CustomRequest extends JsonObjectRequest {
public CustomRequest(int method, String url, String jsonRequest,
Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {
super(method, url, jsonRequest, listener, errorListener);
}
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
// Save http headers
mHeaders = response.headers;
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
private Map<String, String> mHeaders = new HashMap<>();
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return mHeaders;
}
}
and here my test class:
private void testVolleyRequest() {
String url = "http://my-json-feed";
CustomRequest jsonRequest = new CustomRequest
(Request.Method.GET, url, (String)null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d("[TEST]", "Response: " + response.toString());
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d("[TEST]", "error = "+error.toString());
}
});
Volley.newRequestQueue(this).add(jsonRequest);
}
If I try the sample above I keep getting:
com.android.volley.ParseError: org.json.JSONException: Value <html><head><meta of type java.lang.String cannot be converted to JSONObject
but I am returning a JSONObject:
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
if anyone can spot the issue I greatly appreciate.
thx!

thx boukharist,
the url above was not providing a valid json file. I changed to:
String url = "http://httpbin.org/get?site=code&network=tutsplus";
and it works!

Related

Redirect User to a certain activity based on Volley's response code (Android)

I am trying to start an Intent based on the response code from the Server.
All the requests are carried out using Volley. Below is the Custom Request I'm using:
public class JSONObjectRequestUTF8 extends JsonObjectRequest {
public JSONObjectRequestUTF8(int method, String url, JSONObject jsonRequest,
Listener<JSONObject> listener, ErrorListener errorListener) {
super(method, url, jsonRequest, listener,
errorListener);
setShouldCache(false);
}
#Override
protected Response<JSONObject> parseNetworkResponse (NetworkResponse response) {
try {
String utf8String = new String(response.data, "UTF-8");
return Response.success(new JSONObject(utf8String), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
// log error
return Response.error(new ParseError(e));
} catch (JSONException e) {
// log error
return Response.error(new ParseError(e));
}
}
In ParseNetworkResponse the error code comes correctly. However I'm not able to work out how to start an Intent from here.
Any help would be appreciated.
Thank you very much in advance.
And have a nice day.
EDIT: Code for calling the above class
public class AuditAPI implements Response.Listener<JSONObject>, Response.ErrorListener {
private Context mContext;
private SharedPreferences deviceInfo;
public void makeAudit(final Context mContent,String ipAddress, int status){
this.mContext = mContent;
final HashMap<String, String> login_model = SessionManager.getInstance().getUserDetails(mContent);
JsonObjectRequest jsonObjReq = new JSONObjectRequestUTF8(Request.Method.POST, Constants.MAKE_AUDIT , null, this, this){
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
HashMap<String, String> headers = new HashMap<>();
headers.put(Constants.SUBJECTID,
login_model.get(Constants.SUBJECTID));
headers.put(Constants.ACCESSTOKEN,
login_model.get(Constants.ACCESSTOKEN));
headers.put("user-agent","Android");
headers.put("DEVICEID",Constants.DEVICE_ID);
headers.put("VAPPID", Constants.VAPP_ID);
headers.put("LOGIN_USER_NAME", (MyBaseActivity.getmUserId()));
headers.put("STATUS", String.valueOf(status));
headers.put("IPADDRESS",ipAddress);
headers.put("CONTENT_TYPE",Constants.JSON_FROMAT);
return headers;
}
};
jsonObjReq.setRetryPolicy(new DefaultRetryPolicy(1,
DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
MyApplicationController.getNewInstance(mContext).
addToRequestQueue(jsonObjReq, "AUDIT");
}
#Override
public void onErrorResponse(VolleyError volleyError) {
Log.i("Failure", "onErrorResponse: ");
}
#Override
public void onResponse(JSONObject jsonObject) {
Log.i("Success","onResponse Audit");
}}
So I found a fix which was a tad tedious but simple.
Basically I had to override the deliverError method. In that method the errorCode was present, as a result based on that code I was able to perform the redirection.
Also needed to pass the context in the constructor. Needed to make that change in all request calls.
The reason this worked as per my understanding is that deliverError is called when the worker thread needs is trying to merge back to the main UI Thread. This bifurcation is caused in parseNetworkResponse since it's a heavy process which is why a worker thread is assigned to it.
Updated Class:
public class JSONObjectRequestUTF8 extends JsonObjectRequest {
//This is a new parameter
private Context context;
public JSONObjectRequestUTF8(int method, String url, JSONObject jsonRequest,
Listener<JSONObject> listener, ErrorListener errorListener,Context context) {
super(method, url, jsonRequest, listener,
errorListener);
this.context = context;
setShouldCache(false);
}
#Override
protected Response<JSONObject> parseNetworkResponse (NetworkResponse response) {
try {
String utf8String = new String(response.data, "UTF-8");
return Response.success(new JSONObject(utf8String), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
// log error
return Response.error(new ParseError(e));
} catch (JSONException e) {
// log error
return Response.error(new ParseError(e));
}
}
#Override
public void deliverError(VolleyError error) {
if(error.networkResponse.statusCode == HttpURLConnection.HTTP_PRECON_FAILED || error.networkResponse.statusCode == HttpURLConnection.HTTP_UNAUTHORIZED){
Intent i = new Intent(context, LoginActivity.class);
i.putExtra(EReceiptsConstants.ERROR_MESSAGE, EReceiptsConstants.SESSION_MESSAGE);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(i);
return;
}
}

Volley POST Request with headers and raw json body

I am trying to make Post request from Android App that takes username and password in request body with content-type application/json in headers.
I tried changing content-type and how i send username passoword in body, but still no luck
public class MainActivity extends AppCompatActivity {
private Button Login;
private EditText loginEmail, loginPassword;
String URL = "https://localhost:8080/api/v1/auth";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loginEmail = (EditText)findViewById(R.id.etUsername);
loginPassword = (EditText)findViewById(R.id.etPassword);
Login = (Button)findViewById(R.id.btnLogin);
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("username", loginEmail.getText().toString());
} catch (JSONException e) {
e.printStackTrace();
}
try {
jsonObject.put("password", loginPassword.getText().toString());
} catch (JSONException e) {
e.printStackTrace();
}
final String requestBody = jsonObject.toString();
Login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
StringRequest request = new StringRequest(Request.Method.POST, URL, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Toast.makeText(MainActivity.this, response, Toast.LENGTH_LONG).show();
startActivity(new Intent(MainActivity.this, LandingPage.class));
finish();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MainActivity.this, "Error", Toast.LENGTH_LONG).show();
NetworkResponse response = error.networkResponse;
if (error instanceof ServerError && response != null) {
try {
String res = new String(response.data,
HttpHeaderParser.parseCharset(response.headers, "utf-8"));
// Now you can use any deserializer to make sense of data
JSONObject obj = new JSONObject(res);
Toast.makeText(MainActivity.this, res, Toast.LENGTH_LONG).show();
Log.i("MainActivity", res);
//use this json as you want
} catch (UnsupportedEncodingException e1) {
// Couldn't properly decode data to string
e1.printStackTrace();
} catch (JSONException e2) {
// returned data is not JSONObject?
e2.printStackTrace();
}
}
}
})
{
#Override
public String getBodyContentType() {
return "application/json; charset=utf-8";
}
#Override
public byte[] getBody() throws AuthFailureError {
try {
return requestBody == null ? null : requestBody.getBytes("utf-8");
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8");
return null;
}
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("Content-Type", "application/json");
return params;
}
};
RequestQueue rQueue = Volley.newRequestQueue(MainActivity.this);
rQueue.add(request);
}
});
}
}
Post Request from Postman works perfectly fine but it throws Error 400 with Volley.
Below is the error I get in console
"errorSummary":"Bad request. Accept and/or Content-Type headers likely do not match supported values."
Try using a custom request and extending Request<JSONObject>:
CustomRequest class:
public class CustomRequest extends Request<JSONObject> {
private final Map<String, String> mHeaders;
private final JSONObject mBody;
private final Response.Listener<JSONObject> mResponseListener;
public CustomRequest(int method, String url, Map<String, String> headers, JSONObject body, Response.Listener<JSONObject> responseListener, Response.ErrorListener errorListener) {
super(method, url, errorListener);
this.mHeaders = headers;
this.mBody = body;
this.mResponseListener = responseListener;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return mHeaders != null ? mHeaders : super.getHeaders();
}
#Override
public String getBodyContentType() {
return "application/json; charset=UTF-8";
}
#Override
public byte[] getBody() throws AuthFailureError {
return mBody != null ? mBody.toString().getBytes(Charset.forName("UTF-8")) : super.getBody();
}
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException | JSONException e) {
return Response.error(new ParseError(e));
}
}
#Override
protected void deliverResponse(JSONObject response) {
mResponseListener.onResponse(response);
}
}
Usage:
String url = "https://localhost:8080/api/v1/auth";
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("username", loginEmail.getText().toString());
jsonObject.put("password", loginPassword.getText().toString());
} catch (JSONException e) {
e.printStackTrace();
}
CustomRequest customRequest = new CustomRequest(Request.Method.POST, url, headers, jsonObject, response -> {
// put your reponse listener code here
}, error -> {
// put your error listener code here
});
RequestQueue queue = Volley.newRequestQueue(context);
queue.add(customRequest);

Send Basic Autherization in vollery request

Hello I am trying to send Basic Authorization in volley request. I tried this almost 7-8 months ago and it was working fine..
but I don't know what goes wrong now when I try to send request with authorization header and I always get 403 error. and when I tried to hit the url from hurl.it the it works fine.. can someone tell what is wrong with my request.
This is how I am adding Authorization header in volley request
public class VolleyRequest extends JsonObjectRequest {
String email, pass;
boolean saveCookeis;
public VolleyRequest(int method, String url, JSONObject jsonRequest,
Listener<JSONObject> listener, ErrorListener errorListener,
String email, String pass, boolean saveCookie) {
super(method, url, jsonRequest, listener, errorListener);
// TODO Auto-generated constructor stub
this.email = email;
this.pass = pass;
this.saveCookeis = saveCookie;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
// TODO Auto-generated method stub
HashMap<String, String> params = new HashMap<String, String>();
String auth = "";
try {
auth = android.util.Base64.encodeToString(
(this.email + ":" + this.pass).getBytes("UTF-8"),
android.util.Base64.DEFAULT);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
params.put("Authorization", auth);
return params;
}
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
// TODO Auto-generated method stub
if (saveCookeis) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
ApplicationController.getInstance().checkSessionCookie(
response.headers);
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
return super.parseNetworkResponse(response);
}
}
Screenshots From post man
Sample data can found here
Now this code is not working. Can anyone tell me what I am doing wrong. Thanks.
Try this:
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, URL, new JSONObject(), new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
//Handle response
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
//Handle Error
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
return super.getParams();
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put(
"Authorization",
String.format("Basic %s", Base64.encodeToString(
String.format("%s:%s", username, password).getBytes(), Base64.DEFAULT)));
return params;
}
};
jsonObjectRequest.setShouldCache(false);
requestQueue.add(jsonObjectRequest);

Android - Volley JSON request to remote server only works correctly once

I am developing an Android app where I need to fetch results from a MySQL database in a remote server. I have written some PHP scripts to query the database and output the results in JSON format.
The problem is that the request fetches expected results only for the first time. When I send the same request with different POST params, it returns the first result all over again. I modified the PHP script to attach the POST param in the JSON result and tried logging the POST param. It is also the same first param.
If I uninstall the app from the device and re-install, again it returns the correct JSON only for the first time.
NOTE: When i run the same code with the same PHP scripts and same database on localhost (WAMPP), everything works perfect.
I tried using the Google Chrome extension Postman to check the output from the remote server, it worked as expected. So, I can confirm that the problem is with my app/code.
Here's my CustomJSONObjectRequest class :
public class CustomJSONObjectRequest extends Request<JSONObject> {
private Listener<JSONObject> listener;
private Map<String, String> params;
public CustomJSONObjectRequest(String url, Map<String, String> params,
Listener<JSONObject> reponseListener, ErrorListener errorListener) {
super(Method.GET, url, errorListener);
this.listener = reponseListener;
this.params = params;
}
public CustomJSONObjectRequest(int method, String url, Map<String, String> params,
Listener<JSONObject> reponseListener, ErrorListener errorListener) {
super(method, url, errorListener);
this.listener = reponseListener;
this.params = params;
}
protected Map<String, String> getParams()
throws com.android.volley.AuthFailureError {
return params;
};
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
#Override
protected void deliverResponse(JSONObject response) {
listener.onResponse(response);
}
}
And this is how I am sending the request :
String item_name = getIntent().getExtras().getString("item_name");
String url = getString(R.string.remote_server)+"fetch-item-list.php";
CustomJSONObjectRequest jsObjRequest = new CustomJSONObjectRequest(Request.Method.POST, url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
Log.d("item name from intent extra", item_name); //intent extra
Log.d("response", response.toString()); //json response
Log.d("item name from response", response.getString("item_name")); //post parameter. This and the intent extra should be same
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(getApplicationContext(), error.toString(), Toast.LENGTH_SHORT).show();
}
}) {
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<>();
params.put("item_name", item_name);
return params;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> header = new HashMap<String, String>();
header.put("Content-Type", "application/json; charset=utf-8");
return header;
}
};
MyVolleySingleton.getInstance(getApplicationContext()).addToRequestQueue(jsObjRequest);
This is the logcat :
D/item name from intent extra: apple
D/Response: {"data":[{"item_discount":"0.00","item_price":"50.00","unique_id":"181................
D/item name from response: orange
I think I found the solution
Looks like the JSON response was getting cached, so the response for the first request got cached and the next requests were never sent to the server. The cached JSON response was returned for every other requests.
All I had to do was disable caching.
I added the line jsObjRequest.setShouldCache(false); before adding it to the request queue.
jsObjRequest.setShouldCache(false);
MyVolleySingleton.getInstance(getApplicationContext()).addToRequestQueue(jsObjRequest);
Thanks to this question.
But I still do not get it why it worked on localhost without this setting.

How to rewrite my multipart post string request to json object request using volley?

I have a thread here on stack overflow, and I already solved my problem upon using a multipart post through Volley. The problem is, what I have done is a String request and I want it to be change to JSONObject request because I needed to catch the server's response.
UPADATE : I also tried to change all Response<String> to Response<JSONObject>
This is my new implementation at my parseNetworkResponse(NetworkResponse response) method :
#Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response)
{
try {
String jsonString = new String(response.data,
HttpHeaderParser.parseCharset(response.headers));
return Response.success(new JSONObject(jsonString),
HttpHeaderParser.parseCacheHeaders(response));
} catch (UnsupportedEncodingException e) {
return Response.error(new ParseError(e));
} catch (JSONException je) {
return Response.error(new ParseError(je));
}
}
But unfortunately, It started calling the error response method
#Override
public void onErrorResponse(VolleyError error) {
Log.i("Error",error.toString());
}
The error displayed is :
com.android.volley.ParseError: org.json.JSONException: Value <div of type java.lang.String cannot be converted to JSONObject
As my answer in your previous question, I suggest that you create a custom request that reponses NetworkResponse or JSONObject like the following :
MultipartRequest.java:
class MultipartRequest extends Request<NetworkResponse> {
private final Response.Listener<NetworkResponse> mListener;
private final Response.ErrorListener mErrorListener;
private final Map<String, String> mHeaders;
private final String mMimeType;
private final byte[] mMultipartBody;
public MultipartRequest(String url, Map<String, String> headers, String mimeType, byte[] multipartBody, Response.Listener<NetworkResponse> listener, Response.ErrorListener errorListener) {
super(Method.POST, url, errorListener);
this.mListener = listener;
this.mErrorListener = errorListener;
this.mHeaders = headers;
this.mMimeType = mimeType;
this.mMultipartBody = multipartBody;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
return (mHeaders != null) ? mHeaders : super.getHeaders();
}
#Override
public String getBodyContentType() {
return mMimeType;
}
#Override
public byte[] getBody() throws AuthFailureError {
return mMultipartBody;
}
#Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
try {
return Response.success(
response,
HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
#Override
protected void deliverResponse(NetworkResponse response) {
mListener.onResponse(response);
}
#Override
public void deliverError(VolleyError error) {
mErrorListener.onErrorResponse(error);
}
}
Here, you can create a custom MultipartRequest extends Request<JSONObject>
Hope this helps!
UPDATE FOR YOUR COMMENT:
I have a multipart entity is already included in apache http component libraries. Is there is any alternatives?
Here is my Request with HttpEntity and return a JSONArray. I think you can customize to return a JSONObject if you like.
private void makeJsonArrayRequest(Context context, int method, String url, HttpEntity httpEntity, final VolleyResponseListener listener) {
JSONObject jsonRequest = null;
String stringEntity;
try {
stringEntity = EntityUtils.toString(httpEntity);
if (stringEntity != null) {
jsonRequest = new JSONObject(stringEntity);
}
} catch (IOException | JSONException e) {
e.printStackTrace();
}
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(method, url, jsonRequest, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray jsonArray) {
listener.onResponse(jsonArray);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
listener.onError(getErrorMessage(error));
}
}) {
#Override
protected Response<JSONArray> parseNetworkResponse(NetworkResponse response) {
...
}
#Override
protected VolleyError parseNetworkError(VolleyError volleyError) {
...
}
};
// Access the RequestQueue through singleton class.
MySingleton.getInstance(context).addToRequestQueue(jsonArrayRequest);
}

Categories

Resources