Retrofit - How do define hashmap gson response? - android

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.

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())`

Parsing JSON in Retrofit 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

Retrofit HashMap is null [duplicate]

This question already has answers here:
Displaying integer on toast
(4 answers)
Closed 6 years ago.
Hi i'm trying to learn android and currently implementing retrofit and tried to solve this using related post here sadly nothing Helped me pls hear me out.
I have a json data that i need to parse here it is
{
"-KNea90tV5nZlkeqxc3Q": {
"accountName": "Mark Angelo Noquera",
"accountNumber": "12435656443",
"accountType": "Peso Savings"
},
"-KNeaPmBoTXV4mQC6cia": {
"accountName": "Mark Angelo Noquera",
"accountNumber": "12435656444",
"accountType": "Peso Checking"
},
"-KNeaWe_ZbtI9Tn6l-oF": {
"accountName": "Mark Angelo Noquera",
"accountNumber": "12435656445",
"accountType": "Personal Loan"
}}
Then some tutorials told me to use a hashmap so i implemented mine
here is my ModelClass1.class
public class MarkSamples {
public HashMap<String, MarkSample> marksamples;
public HashMap<String, MarkSample> getMarksamples() {
return marksamples;
}}
ModeClass2.class - For handling the objects
public class MarkSample {
#SerializedName("accountName")
public String acntName;
#SerializedName("accountNumber")
public String acntNumber;
#SerializedName("accountType")
public String acntType;
public String getName() {
return (acntName);
}
public void setName(String acntName) {
this.acntName = acntName;
}
public String getNumber() {
return (acntNumber);
}
public void setNumber(String acntNumber) {
this.acntNumber = acntNumber;
}
public String getType() {
return (acntType);
}
public void setType(String acntType) {
this.acntType = acntType;
}
}
My API is here UPDATED
public interface ContactsAPI {
#GET("/api/accounts.json")
public void getSamples(Callback<HashMap<String, MarkSample>> response);}
ANd lastly i'm calling my handler here UPDATED
api.getSamples(new Callback<HashMap<String, MarkSample>>() {
#Override
public void success(HashMap<String, MarkSample> stringMarkSampleHashMap, Response response) {
loading.dismiss();
int mint = stringMarkSampleHashMap.size();
Toast.makeText(mainActivity, mint, Toast.LENGTH_SHORT).show();
}
#Override
public void failure(RetrofitError error) {
}
});
And everytime i TOast the outcome i got a null value did i implemented it wrong? Im certain i used my RootUrl correctly if this is not the problem what are the other methods i can use? please help me.
here is my Logcat UPDATED
FATAL EXCEPTION: main
Process: com.exist.kelvs.retrofit2, PID: 2517
android.content.res.Resources$NotFoundException: String resource ID #0x3
at android.content.res.Resources.getText(Resources.java:312)
at android.widget.Toast.makeText(Toast.java:286)
at com.exist.kelvs.retrofit2.RetrofitHandler$2.success(RetrofitHandler.java:51)
at com.exist.kelvs.retrofit2.RetrofitHandler$2.success(RetrofitHandler.java:46)
at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Update api definition to:
#GET("/api/accounts.json")
public void getSamples(Map<String,MarkSample> response);}
And api call to:
api.getSamples(new Callback<Map<String,MarkSample>() {
#Override
public void success(Map<String,MarkSample> samplelist, Response response) {
loading.dismiss();
int mint = samplelist.size();
...
Update:
To get all accountNames add this code to your callback:
...
loading.dismiss();
int mint = 0;
if (samplelist!=null){
mint = samplelist.size();
for (MarkSample item:samplelist.values()){
Log.d("TEST","value: "+item.getName();
}
}
...
Check out this article:
Getting Starter with Retrofit 2
Use Retrofit2 Call with set up converter factory to enqueue your data
Your problem is in serialization:
There is no marksamples field in respond. You don't need special MarkSamples class. Look how you can modify your code:
public interface ContactsAPI {
#GET("/api/accounts.json")
Call<LinkedHashMap<String, MarkSample>> getSamples();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(" https://pbcom.firebaseio.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
}
and request:
ContactsAPI api = ContactsAPI.retrofit.create(ContactsAPI.class);
Call<LinkedHashMap<String, MarkSample>> call = api.getSamples();
call.enqueue(new Callback<LinkedHashMap<String, MarkSample>>() {
#Override
public void onResponse(Call<LinkedHashMap<String, MarkSample>> call, Response<LinkedHashMap<String, MarkSample>> response) {
LinkedHashMap<String, MarkSample>> samples = response.body();
}
#Override
public void onFailure(Call<LinkedHashMap<String, MarkSample>> call, Throwable t) {
}
});

Strongloop/Loopback findAll feature returns null

I'm developing an Android application in conjunction with Strongloop/Loopback. I've stored my data in a MySQL database and have no problem mapping this with Strongloop/Loopback. However, when retrieving the values from the database using Strongloop/Loopback, the list always return a size but with null values. Can't figure out what is wrong. Can anybody help me with this? Many thanks :)
Here is my json response for the database when access from Strongloop:
[
{
"rewards_image_name": "http://x.x.x.x/projects/images/rewards_1.png",
"rewards_equivalent_points": "10",
"id": 1 }, {
"rewards_image_name": "http://x.x.x.x/projects/images/rewards_2.png",
"rewards_equivalent_points": "20",
"id": 2 }, {
"rewards_image_name": "http://x.x.x.x/projects/images/rewards_3.png",
"rewards_equivalent_points": "30",
"id": 3 }, {
"rewards_image_name": "http://x.x.x.x/projects/images/rewards_4.png",
"rewards_equivalent_points": "40",
"id": 4 }
]
Here is my code for getting the list:
StrongloopClient strongloopClient = new StrongloopClient(this.context);
RestAdapter adapter = strongloopClient.getLoopBackAdapter("Rewards", "GET");
StrongloopRewardsModelRepository strongloopRewardsModelRepository = adapter.createRepository(StrongloopRewardsModelRepository.class);
strongloopRewardsModelRepository.findAll(new ListCallback<StrongloopRewardsModel>() {
#Override
public void onSuccess(List<StrongloopRewardsModel> arg0) {
Log.e("", "GetAllRewards success: " + arg0.get(0).getEquivalentPoints());
}
#Override
public void onError(Throwable arg0) {
Log.e("", "GetAllRewards error: " + arg0);
}
});
Here is the StrongloopClient:
public class StrongloopClient {
private Context context;
private RestAdapter adapter;
public StrongloopClient(Context contxt) {
context = contxt;
}
public RestAdapter getLoopBackAdapter(String transaction, String operation) {
if (adapter == null) {
adapter = new RestAdapter(context, "http://192.168.44.101:3000/api");
adapter.getContract().addItem(
new RestContractItem("/" + transaction, operation),
transaction);
}
return adapter;
}
Here is the code for Repository:
public class StrongloopRewardsModelRepository extends ModelRepository<StrongloopRewardsModel>{
public StrongloopRewardsModelRepository() {
super("Rewards", "Rewards", StrongloopRewardsModel.class);
}
}
And this is the Model:
public class StrongloopRewardsModel extends Model {
private Integer rewardsImageId;
private String rewardImageName;
private String equivalentPoints;
public Integer getRewardsImageId() {
return rewardsImageId;
}
public void setRewardsImageId(Integer rewardsImageId) {
this.rewardsImageId = rewardsImageId;
}
public String getRewardImageName() {
return rewardImageName;
}
public void setRewardImageName(String rewardImageName) {
this.rewardImageName = rewardImageName;
}
public String getEquivalentPoints() {
return equivalentPoints;
}
public void setEquivalentPoints(String equivalentPoints) {
this.equivalentPoints = equivalentPoints;
}
}
Finally, I've found what was wrong. POJOs should match the fields of models created in models.json. Thanks for your time.
However, my other question is that, when I used filters for querying such as passing the filters as part of "parameters" being passed to Strongloop adapter, it seems to return all of the model instances instead of filtered ones. Same code from my question, just that getLoopbackAdapter("Rewards","GET") becomes "Rewards?filter[where][rewards_equivalent_points]=10","GET"). Any ideas why it behaved like that? Thanks :)
This is because your search didn't find any instance. I'm not sure if you need to handle this on your App or if the SDK deals with that.
For example, when trying to search by an element[1] through the rest interface, it gets converted to 404 (not found): rest: {after: convertNullToNotFoundError}
https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/dao.js#L771

Categories

Resources