Parsing JSON in Retrofit android? - android

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

Related

How to POST retrofit return JSONArray with Root

i have a problem to take json with root/parent
Web Service
#POST("products")
Call<List<ProductResponse>> products(#Body ProductRequest productRequest);
My Model for Response
public class ProductResponse{
private String id_prodotto;
private String nome_prodotto;
}
My call response:
public void products(String s){
ProductRequest productRequest = new ProductRequest();
productRequest.setToken(s);
Call <List<ProductResponse>> pR =
ApiClient.getUserService().products(productRequest);
pR.enqueue(new Callback<List<ProductResponse>>() {
#Override
public void onResponse(Call<List<ProductResponse>> call, retrofit2.Response<List<ProductResponse>> response) {
if(response.isSuccessful()){
productList = response.body();
recyclerViewProdotti.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerViewProdotti.setHasFixedSize(true);
adapterProducts = new AdapterProducts(context.getApplicationContext(),productList,tkn);
recyclerViewProdotti.setAdapter(adapterProducts);
}else {
Toast.makeText(context,"Fail Products " + tkn, Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<List<ProductResponse>> call, Throwable t) {
Log.e("PR", t.getLocalizedMessage());
}
}
It's work when my response is:
{ "id_prodotto": "1", "nome_prodotto": "one"}
{ "id_prodotto": "2", "nome_prodotto": "two"}
But need take this response: { products: { "id_prodotto": "1", "nome_prodotto": "one"}
{ "id_prodotto": "2", "nome_prodotto": "two"}}
What i need to do?
You can use List<Object> since your response is like. But you need to remove List at the Webservice.
#POST("products")
Call<ProductListResponse> products(#Body ProductRequest productRequest);
Next, at you need to create new model class named ProductListResponse with using List.
public class ProductListResponse {
#SerializedName("products")
private List<ProductResponse> product;
}

how to handle null or empty json key value response in retrofit 2 response body?

I am using retrofit 2 in android. My app crashes when one of the value return null key like "TOTAL_EXPENSE_AMOUNT": null. My json is like below:
[{
"YEAR": "2019",
"TOTAL_AMOUNT": "1580292",
"TOTAL_EXPENSE_AMOUNT": "1740857",
"TOTAL_BALANCE": "-160565"
},
{
"YEAR": "2018",
"TOTAL_AMOUNT": "144000",
"TOTAL_EXPENSE_AMOUNT": null,
"TOTAL_BALANCE": null
} ]
1..My retrofit Interface is below:
public interface Api {
#POST("User/yearly_balance_expense_list.php")
Call<List<Balance_Data_Model>> getBalanceList();
}
2. My api service is bellow like :
public class ApiService {
public static final String BASE_URL = IPConfigure.getIP();
Api api = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build().create(Api.class);
public Call<List<Balance_Data_Model>> getBalanceList(){
return api.getBalanceList();
}
}
**3.**My projo file is below :
public class Balance_Data_Model {
#SerializedName("YEAR")
#Expose
public String year = "";
#SerializedName("TOTAL_AMOUNT")
#Expose
public String total_amount = "";
#SerializedName("TOTAL_EXPENSE_AMOUNT")
#Expose
public String total_expense = "";
#SerializedName("TOTAL_BALANCE")
#Expose
public String total_balance = "";
//getter/setter here
}
**4.**I have used below code from activity
ApiService apiService = new ApiService();
Call<List<Balance_Data_Model>> call = apiService.getBalanceList();
call.enqueue(new Callback<List<Balance_Data_Model>>() {
#Override
public void onResponse(Call<List<Balance_Data_Model>> list, Response<List<Balance_Data_Model>> response) {
Log.e("success--->","success");
for(Balance_Data_Model m : response.body()){
Log.e("total_expense--->",m.total_expense);
Log.e("total_balance--->",m.total_balance);
}
}
#Override
public void onFailure(Call<List<Balance_Data_Model>> call, Throwable t) {
Log.e("failure--->",t.toString());
}
});
First loop it ok. when total_balance is null then app crashes. How to handle it?
It explained here:
So the proper way to handle this issue is using if-else.
if(m.total_expense!=null)
Log.e("total_expense--->",m.total_expense);
the Log is not print null value , it gives exception , if you want to print the log then use below
Log.e("total_expense--->",m.total_expense + "");
or you can check m.total_expense!=null then print without "".
Please handle all values from response, It is good.
if(response.body() != null){
for(Balance_Data_Model m : response.body()){
if(m.total_expense != null)
Log.e("total_expense--->",m.total_expense);
if(m.total_balance != null)
Log.e("total_balance--->",m.total_balance);
}
}
you can create a method for check the null value
public String amount (String json){
if(json == null){
return "";
else{
retrun json;
}
use like this `String amount=amount(responce.body().getAmount())`

How to handle error response with Retrofit 2?

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)
}

Retrofit - How do define hashmap gson response?

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.

Retrofit: Handling JSON object that dynamically changes its name

I use retrofit.
I have JSON data like this:
{
"elements": {
"159": {
"id": 159,
"name": "Alex"
},
"831": {
"id": 831,
"name": "Sofia"
},
"3125": {
"id": 3125,
"name": "Mark"
}
}
}
Structure of this JSON cannot be configured on my side.
And I want to handle those objects (that dynamically change their names) using retrofit.
I have sth like that now.
Model:
public class Elements{
public ArrayList<ElementsItem> = new Array<ElementsItem>();
//getters setters
public class ElementsItem{
public ArrayList<ElementsItemDetails> = new Array<ElementsItemDetails>();
//getters setters
}
public class ElementItemDetails{
public Integer id;
public String name;
//getters setters
}}
API:
public interface MyApi {
#GET("/file.php?method=getElementDetails")
public void getDetails(#Query("ids") ArrayList<Integer> ids_list, Callback<Elements> callback);
}
And the function where I try to handle data:
public void get_details_for_particular_elements(ArrayList<Integer> ids_list){
Gson gson_gd = new GsonBuilder().registerTypeAdapter(
Elements.class,
new JsonDeserializer<Elements>() {
#Override
public Elementsdeserialize(JsonElement je,
Type type, JsonDeserializationContext jdc)
throws JsonParseException {
// TODO Auto-generated method stub
Log.d("my_app", "Deserialization for Getting Elements Details in progress..");
JsonObject elements= je.getAsJsonObject();
return new Gson().fromJson(elements,
Elements.class);
}
}).create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constants.URL)
.setConverter(new GsonConverter(gson_gd)).build();
MyApi myDetails = restAdapter.create(MyApi.class);
myDetails.getDetails(ids_list, new Callback<Elements>() {
#Override
public void success(Elements e, Response response) {
// TODO Auto-generated method stub
Log.d("my_app", "Success! " + e.getElementsItem().get(0).getElementsItemDetails().get(0).getName());
}
#Override
public void failure(RetrofitError retrofitError) {
// TODO Auto-generated method stub
Log.d("my_app", "Failure..." + retrofitError);
}
});
}
I try to handle the name "Alex" and print it in LogCat, but I cannot - application stops. I am sure that this command:
e.getElementsItem().get(0).getElementsItemDetails().get(0).getName()
is wrong, but I don't know any other way how to handle the name value.
How to behave when object name changes (in that case there are three similar objects called in dependance of list of ids ids_list provided? Here: [156,831,3125]
Please help.
As the key for you ElementItemDetails is dynamically generated, you should use a Map to represent the structure.
In your model class Elements:
Replace
public ArrayList<ElementsItem> = new Array<ElementsItem>();
with
Map<String, ElementItemDetails> elemDetails;
Hope this helps.

Categories

Resources