Convert Object to map for retrofit GET - android

I'm start with Retrofit.
I thought of Jackson but has given me problems and I guess there will Retrofit thought about this
I have GET endpoints.
I need convert this:
public class BaseRequest {
private String param1;
private String param2;
private String param3;
//Getter & Setters ...
}
on a Map <String, String>
i'm using dagger + retrofit.
How do I can do this?
Thanks

You'll need to provide more information on this, do the parameters have the same query param name or different.
If they are different you can just pass them in as a #QueryMap Map<String, String> params being a Map of key/value pairs. Such that the output would be something like
Map<String, String> values = new HashMap<>();
values.put("name1", "value1");
values.put("name2", "value2");
values.put("name3", "value3");
?name1=value1&name2=value2&name3=value3
If they are the same then you need #Query("name") String... values), the output of this would be something like:
List<String> values = new ArrayList<>();
values.add("value1");
values.add("value2");
values.add("value3");
?name=value1&name=value2&name=value3

Related

How to post data using HashMap in retrofit?

Can you please explain how to post data using hashmap in retrofit2 ?
This is what I post
#FormUrlEncoded
#POST("getProfile")
Call<YourResponseObject> getProfile(#FieldMap HashMap<String, String> data);
And the HashMap
HashMap<String, String> map = new HashMap<>();
map.put("token", "yourtoken");
map.put("yourvariable", "yourvariable");
From Retrofit2 documentation check FieldMap for more details
You need to create your interface
public interface YourPostService {
#FormUrlEncoded
#POST("/myEndpoint")
Call<YourResponseClass> postData(#FieldMap Map<String, String> fields);
}
and after this is easy to call and use it

Retrofit 2 - Post FieldMap with Body Object

I want to post a HashMap and a Object using Retrofit.
I tried this code below but received IllegalArgumentException.
#POST("update")
Call<RSP010> postApi010(#FieldMap HashMap<String, String> defaultData, #Body User user);
Logcat
java.lang.IllegalArgumentException: #FieldMap parameters can only be used with form encoding. (parameter #1)
But when I add #FormUrlEncoded. It said
java.lang.IllegalArgumentException: #Body parameters cannot be used with form or multi-part encoding. (parameter #2)
UPDATE CODE
public static HashMap<String, String> defaultData(){
HashMap<String, String> map = new HashMap<>();
map.put("last_get_time", String.valueOf(SharedPreferencesHelper.getLongValue(AppConstants.LAST_GET_UPDATE)));
map.put("duid", SharedPreferencesHelper.getStringValue(AppConstants.DUID));
return map;
My Object which I want to post
int profile_id;
private String name;
private String name_kana; // あいうえお
private int gender; // 1 nam 2 nu
private String birth_day;
private String birth_time;
private String birth_place;
private String relationship;
Explain:
I want to post multiple variables via API to server. FieldMap defaultData for default variables I want to use in every API.
https://futurestud.io/tutorials/retrofit-send-objects-in-request-body I've read this, it said instead of posting all separate variables of an object, I can post an object directly.
You can send #Body User user with #FieldMap HashMap<String, String> defaultData like
String user = new Gson().toJson(user);
HashMap<String, String> map = new HashMap<>();
map.put("last_get_time", String.valueOf(SharedPreferencesHelper.getLongValue(AppConstants.LAST_GET_UPDATE)));
map.put("duid", SharedPreferencesHelper.getStringValue(AppConstants.DUID));
map.put("duid", SharedPreferencesHelper.getStringValue(AppConstants.DUID));
map.put("user", user);
OR
Use #PartMap Map<String, RequestBody>
#Multipart
#POST("update")
Call<RSP010> postApi010(#PartMap Map<String, RequestBody> defaultData);
And create your request parameters
Map<String, RequestBody> map = new HashMap<>();
map.put("last_get_time", toRequestBody(String.valueOf(SharedPreferencesHelper.getLongValue(AppConstants.LAST_GET_UPDATE))));
map.put("duid", toRequestBody(SharedPreferencesHelper.getStringValue(AppConstants.DUID)));
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),new Gson().toJson(user));
map.put("user", body);
// This method converts String to RequestBody
public static RequestBody toRequestBody (String value) {
RequestBody body = RequestBody.create(MediaType.parse("text/plain"), value);
return body ;
}
Modify your method to:
#POST("update")
Call<RSP010> postApi010(#Query("last_get_time") String lastGetTime,
#Query("duid") String uid,
#Body User user);

Naming convention with Firebase serialization/deserialization?

I wonder to know how Firebase serialize/deserialize POJO object to/from json, does it use Jackson or Gson or any similar library else.
I have trouble about naming convention with Firebase. My model some like this:
class Data {
private String someFieldName;
private String anotherFieldName;
public Data() {}
public void setSomeFieldName(String) {...}
public String getSomeFieldName(String) {...}
public void setAnotherFieldName(String) {...}
public String getAnotherFieldName() {...}
}
And the expected result in Firebase should be:
{
"some_field_name" : "...",
"another_field_name" : "..."
}
with Gson I can use FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES for my purpose, as in Gson doc:
Here's a few examples of the form "Java Field Name" ---> "JSON Field Name":
someFieldName ---> some_field_name
_someFieldName ---> _some_field_name
aStringField ---> a_string_field
aURL ---> a_u_r_l
How can I convert my POJO object to "Firebase value" with specific naming convention and vice versa, or there are any way to customize the serialize/deserialize process?
Thanks!
When reading the data back from the Firebase database you can use the #PropertyName annotation to mark a field to be renamed when being serialized/deserialized, like so:
#IgnoreExtraProperties
class Data {
#PropertyName("some_field_name")
public String someFieldName
#PropertyName("another_field_name")
private String anotherFieldName;
public Data() {}
}
Make sure that your field is public and not private or else the annotation will not work (I also believe that Firebase uses Jackson to handle the object mapping under the hood, but don't think you can actually customize HOW it uses it).
Personally I prefer keeping explicit control over the serialization/deserialization process, and not relying on specific framework and/or annotations.
Your Data class can be simply modified like this :
class Data {
private String someFieldName;
private String anotherFieldName;
public Data() {}
public Data(Map<String, Object> map) {
someFieldName = (String) map.get("some_field_name") ;
anotherFieldName = (String) map.get("another_field_name") ;
}
public Map<String, Object> toMap() {
Map<String, Object> map = new HashMap<>();
map.put("some_field_name", someFieldName);
map.put("another_field_name", anotherFieldName);
return map ;
}
}
For writing value to firebase, simply do :
dbref.setValue(data.toMap());
For reading :
Map<String, Object> map = (Map<String, Object>) dataSnapshot.getValue();
data = new Data(map);
They are some advantages with this solution :
No assumption is made on underlying json framework
No need to use annotations
You can even further decouple you Object model from your Json model by externalizing the methods toMap() and constructor to a DataMapper (snippet hereunder)
public static Data fromMap(Map<String, Object> map) {
String someFieldName = (String) map.get("some_field_name") ;
String anotherFieldName = (String) map.get("another_field_name") ;
return new Data(someFieldName, anotherFieldName);
}
public static Map<String, Object> toMap(Data data) {
Map<String, Object> map = new HashMap<>();
map.put("some_field_name", data.getSomeFieldName());
map.put("another_field_name", data.getAnotherFieldName());
return map ;
}

how to add multiple lines which are an array of the same parameter in retrofit?

I want to pass an array of the same element, but i want them to be passed as new fields. I want to do something like:
#POST("/api/userInfo/changeProfileData/")
void updateProfile(
#Field("userInfoProfile[languages][0]") String language1,
#Field("userInfoProfile[languages][1]") String language2,
Callback<BaseModel<StateModel>> callback);
But the problem is that i don't know the number of elements so i cannot do like above.
I tried to pass a list:
#Field("userInfoProfile[languages]") List<String> languages
but the value is not valid because i need fields with their index (userInfoProfile[languages][0]).
Use FieldMap to make field association more dynamic.
Update your interface to:
void updateProfile(
#FieldMap Map<String, String> languages,
Callback<BaseModel<StateModel>> callback);
Method that will build FieldMap out of your list
private Map<String, String> buildFieldMap(List<String> data){
Map<String, String> map = new HashMap<String, String>();
String fieldName = "userInfoProfile[languages][%d]"
for(int i = 0; i < data.size(); i++){
map.put(String.format(fieldName, i), data.get(i));
}
return map;
}

How to parse a JSON with dynamic “key” in android by using GSON

I have been using GSON library to parse all the json string and get a JSON object.
But now I need to parse is like this:
{
"status":1,
"info":[
{
"\u5a31\u4e50":"\u51b7\u76d8,\u9ad8\u811a\u676f,\u6211\u7684\u7cd6\u679c\u5c4b,\u670d\u52a1\u4e1a\u6d88\u8d39\u52b5"
},
{
"\u7f8e\u5986":"\u4e2a\u62a4|\u5316\u5986#\u9762\u90e8\u62a4\u7406,\u4e2a\u4eba\u536b\u751f,\u8eab\u4f53\u62a4\u7406,\u9999\u6c34\u9999\u6c1b,\u6c90\u6d74|\u7f8e\u53d1\u7528\u54c1,\u5f69\u5986,\u7cbe\u6cb9SPA,\u773c\u90e8\u62a4\u7406,\u78e8\u7802\u53bb"
},
{
"\u8863\u670d":"\u670d|\u9970|\u978b|\u5e3d#\u670d\u88c5,\u978b\u9774,\u5185\u8863,\u914d\u9970,\u536b\u8863,\u4f11\u95f2\u88e4,T\u6064,\u88d9\u5b50,\u886c\u886b,\u9488\u7ec7\u886b,\u5a74\u5e7c\u513f\u670d\u9970"
}
],
"total":3
}
The key fields are dynamic, so I don't know how to write a model class to read this.
How would you like your model class to look?
status and total would probably be int, so that only leaves info.
As an experiment, just add a field Object info and see how Gson would set it to an ArrayList<LinkedHashMap<String, String>> -- ugly and hard to access by key, but all the data is there. Given that information, the fastest way to model a class would be:
class Something {
int status;
List<Map<String, String> info;
int total;
}
If you have control over how that JSON is generated, I suggest changing the structure of info from an array of objects [{a:b},{c:d},{e:f}] to just an object {a:b,c:d,e:f}. With this, you could just map it to a Map<String, String> with all the benefits like access by key, keys() and values():
class Something {
int status;
Map<String, String> info;
int total;
}
If you want the latter model class without changing the JSON format, you'll have to write a TypeAdapter (or JsonDeserializer if you're only interested in parsing JSON, not generating it from your model class).
Here's a JsonDeserializer hat would map your original info JSON property to a plain Map<String, String>.
class ArrayOfObjectsToMapDeserializer
implements JsonDeserializer<Map<String, String>> {
public Map<String, String> deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Map<String, String> result = new HashMap<String, String>();
JsonArray array = json.getAsJsonArray();
for (JsonElement element : array) {
JsonObject object = element.getAsJsonObject();
// This does not check if the objects only have one property, so JSON
// like [{a:b,c:d}{e:f}] will become a Map like {a:b,c:d,e:f} as well.
for (Entry<String, JsonElement> entry : object.entrySet()) {
String key = entry.getKey();
String value = entry.getValue().getAsString();
result.put(key, value);
}
}
return result;
}
}
You need to register this custom JsonDeserializer similar to this:
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(
new TypeToken<Map<String, String>>() {}.getType(),
new ArrayOfObjectsToMapDeserializer());
Gson gson = builder.create();
Note that this registers the custom deserializer for any Map<String, String> regardless in what class it is encountered. If you don't want this, you'll need to create a custom TypeAdapterFactory as well and check the declaring class before returning and instance of the deserializer.
Here goes a solution, which does not requires to make a JsonDeserializer.
All you can create is a JsonElement in a map Map.Entry<String, JsonElement>and use a for loop to iterate over the entries
//parsing string response to json object
JsonObject jsonObject = (JsonObject) new JsonParser().parse(jsonString);
//getting root object
JsonObject dateWiseContent = jsonObject.get("rootObject").getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : dateWiseContent.entrySet()) {
//this gets the dynamic keys
String dateKey = entry.getKey();
//you can get any thing now json element,array,object according to json.
JsonArray jsonArrayDates = entry.getValue().getAsJsonArray();
}
read more here

Categories

Resources