I'm using Retrofit 2 and I need to send request body. The problem is somehow the value is converted to string. On the example below, you can see that items and totalPrice which should be array and number respectively are converted to string.
{ cashierId: 'fff7079c-3fc2-453e-99eb-287521feee63',
items: '[{"amount":3,"id":"602a79e3-b4c1-4161-a082-92202f92d1d6","name":"Play Station Portable","price":1500000.0}]',
paymentMethod: 'Debt',
totalPrice: '4500000.0' }
The desired request body is
{ cashierId: 'fff7079c-3fc2-453e-99eb-287521feee63',
items: [{"amount":3,"id":"602a79e3-b4c1-4161-a082-92202f92d1d6","name":"Play Station Portable","price":1500000.0}],
paymentMethod: 'Debt',
totalPrice: 4500000.0 }
Here's the service
#POST("api/sales")
#FormUrlEncoded
Call<Sale> createSale(
#FieldMap Map<String, Object> fields
);
And this is how I call createSale
Map<String, Object> fields = new HashMap<>();
fields.put("cashierId", UUID.fromString("fff7079c-3fc2-453e-99eb-287521feeaaa"));
fields.put("totalPrice", totalPrice);
fields.put("paymentMethod", paymentMethod);
fields.put("items", jsonArray);
Call<Sale> call = retailService.createSale(fields);
Is it possible to send those values as number and array, not as string?
The conversion most certainly happens because you are using #FormUrlEncoded.
According to the documentation:
Field names and values will be UTF-8 encoded before being URI-encoded in accordance to RFC-3986.
A solution would be to use a model class instead of a Map. I see you already have a Sale class. If it looks like something like this:
public class Sale {
String cashierId;
int totalPrice;
String paymentMethod;
ArrayList<SomeObject> items;
}
you can simply do like this:
// in service
#POST("api/sales")
Call<Sale> createSale(#Body Sale sale);
// when doing the call
Sale sale = new Sale();
// set everything in your object
// then
Call<Sale> call = retailService.createSale(sale);
Related
I'd quickly explain what's going on in the JSON data above;
I have a table called messages, with some messages having a common message_id column. I grouped the messages by message_id. the boxes in red are the message_id's which have children
Now onto the question;
is it possible to access the children of the various arrays of message_id, without actually using the message_id string?
i.e iterate over the arrays
while (i < array.length) {
array[i]
}
if it's possible how can I do it?
Below is how I currently get the first array from the data object using the array id exactly
val jsonObject = JSONObject(response)
if (!jsonObject.getBoolean("error")) {
//getting data array from json response object
val dataObject = jsonObject.getJSONObject("data")
Log.i("MessageFragment", "[][] data array " + dataObject)
val array = dataObject.getJSONArray("NzbyxhmodN")
var i = 0
while (i < array.length()) {
//getting wallet object from json array
val message = array.getJSONObject(i)
//adding the wallet to wallet list
messageList!!.add(Message(
message.getInt("id"),
message.getInt("sender_id"),
message.getInt("receiver_id"),
message.getString("subject"),
message.getString("message"),
message.getString("message_id"),
message.getString("timestamp"),
message.getBoolean("isRead")
))
i++
}
I want to get the arrays without using the name i.e ("NzbyxhmodN")
Unfortunately, you cannot model without knowing the key value.
In such cases, I use this approach. It will be useful to you.
// data -> server json response
Iterator keys = data.keys();
while(keys.hasNext()) {
// random key
String key = (String)keys.next();
// and value...
JSONArray value = data.getJSONArray(key);
}
Why is price null here?
JSON is retrieved from here (you could also use multiple parameters, that is why the below object uses Maps; example). Here is CoinGeckoCoinPrice:
public class CoinGeckoCoinPrice {
#Expose
private Map<String, Map<String, Double>> price;
public double getPrice(String slug, String pair) {
...
}
}
Here is my Retrofit2 call:
#GET("/api/v3/simple/price")
Call<CoinGeckoCoinPrice> getCoinPrice(#Query("ids") String coinSlugs, #Query("vs_currencies") String pairs);
I checked logcat for any GSON error message and there is none. So why would price be null?
It works if I change Retrofit2 call to use JsonObject:
#GET("/api/v3/simple/price")
Call<JsonObject> getCoinPrice(#Query("ids") String coinSlugs, #Query("vs_currencies") String pairs);
But I want to wrap the object so I can write data retrieval functions in the CoinGeckoCoinPrice class.
The Json you are getting back doesn't have a price property on it, so Gson doesn't know that it should be parsing the bitcoin object and its price in USD to that object. Given that your returned data can be for any key (i.e. bitcoin, ethereum...) and its properties can also have any keys (i.e. usd, euros...) you'll need to manually parse this by giving Gson a custom adapter to work with.
I need to make GET request using Retrofit and one of the parameters is an array of key-value par, or actually, an array of arrays, that looks like this:
[ [1, 10], [50, 100] ]
I figured out how to perform this request in POSTMAN:
Key: durations[0][0] Value: 1
Key: durations[0][1] Value: 10
and so on.
However, I have no idea ho to represent this for Retrofit.
Thank you for help.
EDIT:
I tried to represent it as #Query("durations[]") List<List<Integer>> duration; Single entry is a List with two values, start and end. No luck.
I advise you to use this method
Object get( #Query("durations[0][0]") int value1,#Query("durations[0][1]") int value2,
#Query("durations[1][0]") int value3,#Query("durations[1][1]") int value4)
So basically this is how I solved this. I just manually built the parameter strings I need and put it into a Map along with value. And in your Retrofit interface make filed as #QueryMap:
#QueryMap Map<String, Integer> durations,
Have a look at my method that makes parameters:
public void prepareDurations(Pair<Integer, Integer> durationValues) {
Map<String, Integer> paramsMap = new HashMap<>();
if(durationValues!=null) {
paramsMap.put("durations[0][0]", durationValues.first);
paramsMap.put("durations[0][1]", durationValues.second);
setDuration(paramsMap);
}else {
setDuration(new HashMap<>());
}
}
Ok so I have this piece of JSON that I want to parse with Gson. I would like the Strings to be the values and the longs to be the keys.
{"completed_questions":[["String",12345],...]}
The issue is the data type, when I try a Map<String, Long> it parses everything but gives me an error because of the duplicate String keys.
I tried to reverse it thinking Gson would know to switch them around but when I tried Map<Long, String> I got an error about not being able to parse my Strings as Longs.
To get it to work I created a swap map class that takes the Key and Value types and swaps them like so public class SwapMap<K, V> implements Map<K, V> however translating the swapped map actions like put/get/remove seem to be pretty difficult to make work.
What's the best way to parse this with Gson even though the strings aren't unique? (But the numbers are)
JSON doesn't allow identical keys on the same level in a json object. It seems like you are trying to map a json array to a java map.
Based on the following data structure, you would need a list if you want to use the default conversion provided by Gson.
{
"completed_questions": [
[
"String",
12345
],
[
"String",
12345
]
]
}
Here is a quick implementation:
private static void mapToObject() {
String json = "{\"completed_questions\":[[\"String\",12345],[\"String\",123456]]}";
Gson gson = new Gson();
CompletedQuestions questions = gson.fromJson(json, CompletedQuestions.class);
for (List<String> arr : questions.getCompleted_questions()) {
for (String val : arr) {
System.out.print(val + " ");
}
System.out.println();
}
}
public static class CompletedQuestions {
List<List<String>> completed_questions;
public List<List<String>> getCompleted_questions() {
return completed_questions;
}
}
This outputs:
String 12345
String 123456
The thing to note is that I am using a list for mapping purposes which closely resembles the data model provided.
This will require you to do the conversion to long yourself. But the way that json string looks. It seems like you would need to operate on the indices. If you have control over the json structure, I would recommending creating a better model. Other wise you can do something like list.get(0) -> your key list.get(1) -> your value which is the long on the inner list.
So what I did is just made a custom Gson Deserializer that mapped these values to a LongSparseArray<String>, which is the best way to go about it.
This is the relevant parts of the Deserializer:
for (JsonElement array : jsonObject.get("my_key").getAsJsonArray()) {
if (array.getAsJsonArray().size() == 2) {
String value = array.getAsJsonArray().get(VALUE).getAsString();
long key = array.getAsJsonArray().get(KEY).getAsLong();
progress.completedActivities.put(key, value);
}
}
Then I just added it to my Gson creator like so:
#Provides #Singleton Gson provideGson() {
return new GsonBuilder()
.registerTypeAdapter(MyClass.class, new MyClass())
.create();
}
I need to send a list / an array of Integer values with Retrofit to the server (via POST)
I do it this way:
#FormUrlEncoded
#POST("/profile/searchProfile")
Call<ResponseBody> postSearchProfile(
#Field("age") List<Integer> age
};
and send it like this:
ArrayList<Integer> ages = new ArrayList<>();
ages.add(20);
ages.add(30);
ISearchProfilePost iSearchProfile = gsonServerAPIRetrofit.create(ISearchProfilePost.class);
Call<ResponseBody> call = iSearchProfile.postSearchProfile(
ages
);
The problem is, the values reach the server not comma separated. So the values there are like age: 2030 instead of age: 20, 30.
I was reading (e.g. here https://stackoverflow.com/a/37254442/1565635) that some had success by writing the parameter with [] like an array but that leads only to parameters called age[] : 2030.
I also tried using Arrays as well as Lists with Strings. Same problem. Everything comes directly in one entry.
So what can I do?
To send as an Object
This is your ISearchProfilePost.class
#FormUrlEncoded
#POST("/profile/searchProfile")
Call<ResponseBody> postSearchProfile(#Body ArrayListAge ages);
Here you will enter the post data in pojo class
public class ArrayListAge{
#SerializedName("age")
#Expose
private ArrayList<String> ages;
public ArrayListAge(ArrayList<String> ages) {
this.ages=ages;
}
}
Your retrofit call class
ArrayList<Integer> ages = new ArrayList<>();
ages.add(20);
ages.add(30);
ArrayListAge arrayListAge = new ArrayListAge(ages);
ISearchProfilePost iSearchProfile = gsonServerAPIRetrofit.create(ISearchProfilePost.class);
Call<ResponseBody> call = iSearchProfile.postSearchProfile(arrayListAge);
To send as an Array List check this link https://github.com/square/retrofit/issues/1064
You forget to add age[]
#FormUrlEncoded
#POST("/profile/searchProfile")
Call<ResponseBody> postSearchProfile(
#Field("age[]") List<Integer> age
};
Retrofit can do this now at least I tested with this -> implementation 'com.squareup.retrofit2:retrofit:2.1.0'
For example
#FormUrlEncoded
#POST("index.php?action=item")
Call<Reply> updateStartManyItem(#Header("Authorization") String auth_token, #Field("items[]") List<Integer> items, #Field("method") String method);
This is the piece we are looking at.
#Field("items[]") List<Integer> items