im doing a test using retrofit and a mock api, i have already a recyclerview with a list of products and the detail of the product while clicking on the row, but in some products the josn is a error handling example :
http://mocklab.io/product/1/
{
"success": true,
"metadata": {
"sku": "2",
"name": "Huawei P20 Pro",
"max_saving_percentage": 30,
"price": 13996,
"special_price": 7990,
"brand": "Huawei",
"rating": {
"average": 4,
"ratings_total": 265
},
but lets say in my case product "3" give me the error json :
http:mocklab.io/product/3/
{
"success": false,
"messages": {
"error": {
"reason": "PRODUCT_NOT_FOUND",
"message": "Product not found!"
}
}
}
the pojos are already done with jsonschema2pojo, so the question is if i click in the 3 row in the recyclerview my app crash because and i get null object reference, but what i want is to hadle the error they ask that is : HTTP 200 - Success false , sorry if i didnt explain myself the best way, and thank you!
call:
GetPhoneDataService service = RetrofitInstance.getRetrofitInstance().create(GetPhoneDataService.class);
Call<APIReponse> call = service.getAllPhones(1);
call.enqueue(new Callback<APIReponse>() {
#Override
public void onResponse(Call<APIReponse> call, Response<APIReponse> response) {
if (response.isSuccessful() && response.body() != null) {
for (Result result : response.body().getMdata().getResults()) {
phoneList.add(result);
}
adapter.setOnClickListener(new PhonesAdapter.OnItemClickListener() {
#Override
public void onItemClick(int position) {
String sku = phoneList.get(position).getSku();
Intent intent = new Intent(MainActivity.this, DetailsActivity.class);
intent.putExtra("sku", sku);
startActivity(intent);
}
});
adapter.notifyDataSetChanged();
} else {
try {
String error = response.errorBody().string();
if (error != null) {
Log.e("Error : ", error);
}
//show error to the user
} catch (Exception e) {
e.printStackTrace();
}
In Retrofit you can handle errors response like this. Inside onResponse callback:
if (response.isSuccessful()
&& response.body() != null) {
//Call was successful and body has data
} else {
try {
String error = response.errorBody().string();
if(error != null)
//show error to the user
} catch (Exception e) {
e.printStackTrace();
}
}
But if your error is thrown here:
public void onItemClick(int position) {
String sku = phonesList.get(position).getSku();
Log.e("ffff", "----- " + sku);
Intent intent = new Intent(MainActivity.this,DetailsActivity.class);
intent.putExtra("sku", sku);
startActivity(intent);
}
then you have problems with not designing your code very well. I don't know context of your code more precisely context when you call generatePhonesList method but, if you need to call it more then once, then consider not setting adapter and recyclerview inside that method. For that you have something called: notifyDataSetChanged()
Related
I am having a issue to get response by using retrofit library.
My JSON is below
{
"regData": [
{
"registrationType": "User",
"userDetails": {
"city": "Bangalore ",
"country": "Bangalore ",
"email": "hfhg#lll.com",
"name": "Cxhcc",
"password": "123456",
"phoneno": "486586"
},
"vehicleRegsiration": {}
}
]
}
Please note that above json i got after debug of my application and when i used same JSON with postman rest client application, it is working fine.
Below is my code
public void postRegistrationData(Registeration reqBody){
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<Long> call = apiService.registrationApi(reqBody);
call.enqueue(new Callback<Long>() {
#Override
public void onResponse(Call<Long> call, Response<Long> response) {
if(response.isSuccessful()){
Long id = response.body().longValue();
Log.d(TAG, "Response: " + id);
} else{
Log.d(TAG, "Response failed: ");
}
}
#Override
public void onFailure(Call<Long>call, Throwable t) {
Log.e(TAG, t.toString());
}
});
}
After execution of above code its always executing below code
Log.d(TAG, "Response failed: ");
public Registeration prepareRegRequestBody(UserDetails uDetail , VehicleRegsiration vDedail, String regType){
RegDatum reg = new RegDatum();
List<RegDatum> regList = new ArrayList<RegDatum>();
Registeration regUV = new Registeration();
reg.setRegistrationType(regType);
reg.setUserDetails(uDetail);
reg.setVehicleRegsiration(vDedail);
regList.add(reg);
regUV.setRegData(regList);
return regUV;
}
public Registeration inputDataUV(){
UserDetails userDetail = new UserDetails();
VehicleRegsiration vehicleRegsiration = new VehicleRegsiration();
Registeration reqbody = new Registeration();
if(userType.getSelectedItem().toString().equalsIgnoreCase("User")){
userDetail.setEmail(signupInputEmail.getText().toString());
userDetail.setPassword(signupInputPassword.getText().toString());
userDetail.setPhoneno(signupInputMobile.getText().toString());
userDetail.setName(signupInputName.getText().toString());
userDetail.setCountry(countryName1.getText().toString());
userDetail.setCity(signupInputCity.getText().toString());
reqbody = prepareRegRequestBody(userDetail, vehicleRegsiration, "User" );
return reqbody;
} else if(userType.getSelectedItem().toString().equalsIgnoreCase("Vehicle")){
vehicleRegsiration.setVehicleOwner(vSignupEmailId.getText().toString());
vehicleRegsiration.setVehicleNoPlate(vSignupVnumber.getText().toString());
vehicleRegsiration.setVehicleType(vSignupType.getText().toString());
vehicleRegsiration.setImeiNo(Long.parseLong(vSignupImeiNo.getText().toString()));
vehicleRegsiration.setPassword(vSignupPassword.getText().toString());
vehicleRegsiration.setCountry(vcountryName1.getText().toString());
vehicleRegsiration.setCity(vSignupCity.getText().toString());
reqbody = prepareRegRequestBody(userDetail, vehicleRegsiration, "Vehicle" );
return reqbody;
} else{
return reqbody;
}
}
Where i am doing wrong please help me get rid of this issue.
Below is the Response which I am getting from REST API
{
"statusCode": "200",
"statusData": "Updated Successfully",
"responseData": {
"bookingId": null,
"customerName": null,
"customerMobileNumber": null,
"patientMobileNumber": null,
"driverMobileNumber": "9090909090",
"deviceToken": "f4kc2epndRA:APA91bEXrl4OmlfRUU2M_mfj3khz53_-OgrOk_lpdYWg8hz4hN4yWEogL3eMobH9nPERyO13UN88zQQDUZn262VBv3gICv9BcNECppuJ_G0SlE-1qn57X5w9kMN_q9MXb6ilXQnlIl0A",
"bookedBy": null,
"emergencyType": null,
"emergencyLatitude": 17.443277115352867,
"emergencyLongitude": 78.45206458121537,
"emergencyGeoHashCode": null,
"hospital": null,
"customerId": 0,
"bookingForSelf": 0,
"bookingStatus": "ACCEPTED",
"customerApp": null,
"groupTypeId": 0,
"groupId": 0,
"gcmToken": null,
"hospitalLatitude": 17.8979999,
"hospitalLongitude": 78.7879989,
"hospitalGeoHashCode": null,
"vehicleNumber": null,
"driverName": "santhosh",
"ambulance_start_latitude": 17.786889,
"ambulance_start_longitude": 78.786999,
"vehicle_id": 1,
"booking_id": 201708240006,
"accepted_datetime": 1503556227596,
"vehicleAttributes": null,
"driverImage": "XXXXXX/pic1.png",
"duration": null,
"distance": null
}
}
Below is the Code which I have tried, BookingAcceptedResponseData is the POJO Class
final Call<BookingAcceptedResponseData> upDBooking = apiService.updateBookingStat(updateBookingMap);
upDBooking.enqueue(new Callback<BookingAcceptedResponseData>() {
#Override
public void onResponse(Call<BookingAcceptedResponseData> call, Response<BookingAcceptedResponseData> response) {
System.out.println("####### Basic updateBookingStat response.body() : "+response.body());
System.out.println("####### Basic toString updateBookingStat response.body() : "+response.body().toString());
try {
if (response.body() != null) {
System.out.println("####### updateBookingStat response.body() : "+response.body().toString());
Intent it = new Intent(MainActivity.this, DashBoardActivity.class);
it.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//it.putExtra("bookingDetails", bookingDetails);
finish();
}
}catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onFailure(Call<BookingAcceptedResponseData> call, Throwable t) {
}
});
I am getting Response as packageName#XXXX from REST API Call I am not getting the actual data.Please help me.
Your Response JSON Object contains JSON Object name responseData to get responseData you need to make below change. try this might help you.
Change response BookingAcceptedResponseData to below BookingAcceptedResponse class
public class BookingAcceptedResponse{
private int statusCode;
private String statusData;
private BookingAcceptedResponseData responseData;
//TODO
//generate getter and setter
}
Inside retro call response method change
response.body().getResponseData();
This will return you proper data
Try getting the response like
response.body().string()
How to handle error response with Retrofit 2 using synchronous request?
I need process response that in normal case return pets array and if request has bad parametrs return error json object. How can I process this two situations?
I am trying to use this tutorial but the main problem is mapping normal and error json to objects.
My normal response example:
[ {
"type" : "cat",
"color": "black"
},
{
"type" : "cat",
"color": "white"
} ]
Error response example:
{"error" = "-1", error_description = "Low version"}
What I got:
Call<List<Pet>> call = getApiService().getPet(1);
Response<List<Pet>> response;
List<Pet> result = null;
try {
response = call.execute(); //line with exception "Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path"
if(!response.isSuccessful()){
Error error = parseError(response);
Log.d("error message", error.getErrorDescription());
}
if (response.code() == 200) {
result = response.body();
}
} catch (IOException e) {
e.printStackTrace();
}
Retrofit 2 has a different concept of handling "successful" requests than Retrofit 1. In Retrofit 2, all requests that can be executed (sent to the API) and for which you’re receiving a response are seen as "successful". That means, for these requests, the onResponse callback is fired and you need to manually check whether the request is actually successful (status 200-299) or erroneous (status 400-599).
If the request finished successfully, we can use the response object and do whatever we wanted. In case the error actually failed (remember, status 400-599), we want to show the user appropriate information about the issue.
For more details refer this link
After going through a number of solutions. Am posting it for more dynamic use. Hope this will help you guys.
My Error Response
{
"severity": 0,
"errorMessage": "Incorrect Credentials (Login ID or Passowrd)"
}
Below is as usual call method
private void makeLoginCall() {
loginCall = RetrofitSingleton.getAPI().sendLogin(loginjsonObject);
loginCall.enqueue(new Callback<Login>() {
#Override
public void onResponse(Call<Login> call, Response<Login> response) {
if (response != null && response.code() == 200){
//Success handling
}
else if (!response.isSuccessful()){
mServerResponseCode = response.code();
Util.Logd("In catch of login else " + response.message());
/*
* Below line send respnse to Util class which return a specific error string
* this error string is then sent back to main activity(Class responsible for fundtionality)
* */
mServerMessage = Util.parseError(response) ;
mLoginWebMutableData.postValue(null);
loginCall = null;
}
}
#Override
public void onFailure(Call<Login> call, Throwable t) {
Util.Logd("In catch of login " + t.getMessage());
mLoginWebMutableData.postValue(null);
mServerMessage = t.getMessage();
loginCall = null;
}
});
}
Below Is util class to handle parsing
public static String parseError(Response<?> response){
String errorMsg = null;
try {
JSONObject jObjError = new JSONObject(response.errorBody().string());
errorMsg = jObjError.getString("errorMessage");
Util.Logd(jObjError.getString("errorMessage"));
return errorMsg ;
} catch (Exception e) {
Util.Logd(e.getMessage());
}
return errorMsg;
}
Below in viewModel observer
private void observeLogin() {
loginViewModel.getmLoginVModelMutableData().observe(this, login -> {
if (loginViewModel.getSerResponseCode() != null) {
if (loginViewModel.getSerResponseCode().equals(Constants.OK)) {
if (login != null) {
//Your logic here
}
}
//getting parsed response message from client to handling class
else {
Util.stopProgress(this);
Snackbar snackbar = Snackbar.make(view, loginViewModel.getmServerVModelMessage(), BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, v -> { });
snackbar.show();
}
} else {
Util.stopProgress(this);
Snackbar snackbar = Snackbar.make(view, "Some Unknown Error occured", BaseTransientBottomBar.LENGTH_INDEFINITE).setAction(android.R.string.ok, v -> { });
snackbar.show();
}
});
}
I think you should create a generic response class (let's say GenericResponse), that is extended by a specific response class (let's say PetResponse). In the first one, you include generic attributes (error, error_description), and in the latter, you put your specific response data (List<Pet>).
In your case, I would go with something like this:
class GenericResponse {
int error;
String error_description;
}
class PetResponse extends GenericResponse {
List<Pet> data;
}
So, your successful response body should look like this:
{
"data": [ {
"type" : "cat",
"color": "black"
},
{
"type" : "cat",
"color": "white"
} ]
}
And your error response body should look like this:
{ "error" = "-1", error_description = "Low version"}
Finally, your response object that is returned from the API call should be:
Response<PetResponse> response;
Wrap all your calls in retrofit2.Response like so:
#POST("user/login")
suspend fun login(#Body form: Login): Response<LoginResponse>
A very simple retrofit 2 error handler:
fun <T> retrofitErrorHandler(res: Response<T>): T {
if (res.isSuccessful) {
return res.body()!!
} else {
val errMsg = res.errorBody()?.string()?.let {
JSONObject(it).getString("error") // or whatever your message is
} ?: run {
res.code().toString()
}
throw Exception(errMsg)
}
}
Then use them like:
try {
createdReport = retrofitErrorHandler(api...)
} catch (e: Exception) {
toastException(ctx = ctx, error = e)
}
I'm trying to parse the following JSON structure using Retrofit on android.
{
"payload": [
{
"name": "Rice",
"brands": [
{
"name": "Dawat",
"subProducts": [
{
"id": 1,
"name": "Basmati Long Grain",
"creditDays": 20,
"currency": "$",
"willDeliver": false,
"minPrice": 250,
"maxPrice": 400,
"sku": "1Kg",
"uom": ""
}
]
}
]
}
],
"messages": []
}
I have made models using http://www.jsonschema2pojo.org/. The keys I'm particularly are payload-->name, brands-->name and subProducts-->name. Below is what I've tried so far. Can anyone please help? I can't parse this JSON Structure using retrofit
productDetails.enqueue(new Callback<GetProductDetailsByPhoneNumber>() {
#Override
public void onResponse(Call<GetProductDetailsByPhoneNumber> call, Response<GetProductDetailsByPhoneNumber> response) {
Toast.makeText(getApplicationContext(), "Response mil gaya", Toast.LENGTH_LONG);
List<Payload> subProducts = new ArrayList<Payload>(response.body().payload);
}
#Override
public void onFailure(Call<GetProductDetailsByPhoneNumber> call, Throwable t) {
}
});
Interface:
#GET("wholesaler/getProductDetailsByPhoneNumber")
Call<GetProductDetailsByPhoneNumber> getProducts(#Query("phoneNumber") String number);
getDService()
public API getDService(){
/**
* The Retrofit class generates an implementation of the API interface.
*/
if(service == null){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
service = retrofit.create(API.class);
}
return service;
}
Payload.java
public class Payload {
public String name;
public List<Brand> brands;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Brand> getBrands() {
return brands;
}
public void setBrands(List<Brand> brands) {
this.brands = brands;
}
}
Try using this, as you are not providing your "Payload"o bject
productDetails.enqueue(new Callback<JsonObject>() {
#Override
public void onResponse(Call<JsonObject> call, Response<JsonObject> response) {
if(response.body()!=null{
JsonObject jsonObject=response.body();
if(response.code() == 200){
if(jsonObject.has("payload"){
JsonArray dataArray = jsonObject.getAsJsonArray(HAS_DATA);
if (dataArray.size() > 0) {
Toast.makeText(getApplicationContext(), "Response Called", Toast.LENGTH_LONG);
//your further code
}
}
}
}
}
#Override
public void onFailure(Call<JsonObject> call, Throwable t) {
//logic for if response fails
}
});
Use this code in your onResponse:
if(response.code()==HttpsURLConnection.HTTP_OK) { //HttpOk is the response code for 200
if (response.body() != null) {
if (response.body().payload!= null) {
//data is there in an array of type payload
//save all the data there
//like you want the name, you can get that by response.body().payload.name and you will get "rice"
//similarly if you want the name which is in subproducts array use : response.body().payload.get(0).brands.get(0).subProducts.get(0).name and you
// will get "Basmati Long Grain"
}
}
}
The code will help you deserialise all the data from the JSON and you can store that wherever you want. Plus I will recommend you to keep a check on other response codes as well(such as 400 for bad request, 500 for internal server error etc). See here, you have your payload in an array, and there was only one element in it, that is why I have used payload.get(0). In case of multiple elements in the array you need to use a loop and then fetch the values, same goes for your brands and subProduct array.
You are trying to get an object PayLoad, and you have
{
"payload": [
{
"name"
this means it doesn't start with a parent, so you need to save the answer in a List like List and letter in the iteration you can use Response.get(position) <-- and this one going to be your payload number position, I hope this can help you
I've looked at this post and need some clarification.
I have a structure that looks like this:
{
"contacts": [
{
"account_id": 3599,
"created_at": 1427556844,
"name": "John Smith",
},
{
"account_id": 3599,
"created_at": 1427155837,
"name": "Carl Johnson",
}
]
}
And I have created it this way:
public class Contacts {
#SerializedName("contacts")
public List<User> contacts;
}
public class User {
#SerializedName("account_id")
int accountId;
#SerializedName("created_at")
String createdAt;
#SerializedName("name")
String name;
}
But when I try to run it with retrofit I get "Retrofit Expected BEGIN_OBJECT but was BEGIN_ARRAY". According to this post my syntax is correct. But I more into Jake Whartons solution (from the other post mentioned) here, that it actually is a hashmap
Map<String, List<User>>
But changing the contacts object to use Hashmap instead gives me the following error: "Expected BEGIN_ARRAY but was BEGIN_OBJECT". So please help me figure out how to define the objects using retrofit and robospice.
Edited:
I'm using robospice, so I have this:
#Override
public Contacts loadDataFromNetwork() throws Exception {
final AlertPolicies[] myIncidents = {null};
return getService().getContacts();
}
And in the activity I have defined in onStart():
spiceManager.execute(contactsRequest, CACHE_KEY, DurationInMillis.ONE_MINUTE, new ContactsRequestListener());
and the Listener like this:
private final class ContactsRequestListener implements RequestListener<Contacts> {
#Override
public void onRequestFailure(SpiceException spiceException) {
if(Constant.DEBUG) Log.d(TAG, "onRequestFailure: " + spiceException.getMessage());
Toast.makeText(ContactsActivity.this, "failure", Toast.LENGTH_SHORT).show();
}
#Override
public void onRequestSuccess(Contacts contacts) {
if(Constant.DEBUG) Log.d(TAG, "onRequestSuccess");
Toast.makeText(AlertPoliciesActivity.this, "success", Toast.LENGTH_SHORT).show();
if(contacts != null) {
updateContacts(contacts);
}
}
}
Contacts is always null and if I look at the response it says "Retrofit Expected BEGIN_OBJECT but was BEGIN_ARRAY" and trying the other way as I explained above gives me the other error.
HashMap<Integer,User> hash=new HashMap();
#Override
public void onRequestSuccess(Contacts contacts) {
if(Constant.DEBUG) Log.d(TAG, "onRequestSuccess");
Toast.makeText(AlertPoliciesActivity.this, "success", Toast.LENGTH_SHORT).show();
if(contacts != null) {
for(int i=0;i<contacts.size();i++){
hash.put(contacts.contacts.get(i).accountId,contacts.contacts);
}
}
}
Thanks, but I think the trick, without having to use callback at all, was actually:
#SerializedName("contacts")
public List<User> contacts;
But I'll keep your hashmap in mind.