Retrofit, how to parse JSON objects with variable keys - android

First I know my title is bad as I didn't come up with better, I'm opened to suggestion.
I'm using retrofit to get data from an api of this kind : #GET("users/{userid}")
It works fine and I'm happy with it, the problem is when I call the same api with #POST("users/widget") with a list of ids. I have the following answer :
{
"long_hash_id": {
"_id": "long_hash_id"
.......
},
"long_hash_id": {
"_id": "long_hash_id",
.....
},
........
}
the "long_hash_id" is typicaly "525558cf8ecd651095af7954"
it correspond to the id of the user attached to it.
When I didn't use retrofit, I used Gson in stream mode to get each user one by one. But I don't know how to tell retrofit.
Hope I'm clear and
Thank you in advance.
----------- Solution :
I made my interface this way :
#FormUrlEncoded
#POST(AppConstants.ROUTE_USER_GROUP)
Call<Map<String,User>> getUsers( #Field("ids") List<String> param, #QueryMap Map<String,String> options);
and I simply gave my ArrayList of ids. Thank you very much

Gson is able to deal with JSON objects with variable keys like the one you posted. What you have to do, in this case, is to declare a Map<String, ModelClass>, where ModelClass is the content of the JSONObject you want to represent

Related

Android. How to parse JsonArrays inside JsonArray

I use retrofit and json. I need to parse a json like that:
{
"outer_array": [
"one" : [{}, {}],
"two" : [{}],
"three" : [{}],
....
]
}
Here is part of my code:
#SerializedName("outer_array") var data: List<List<Data>>
But how can I parse each element of the "outer_array" array by key. Arrays inside "outer_array" can be from 0 to n. Maybe I should use a TypeAdapter. But I don't know how to apply it for this case, please help me
The JSON you've provided is not valid. You can check it on JSONLint. I guess it should be either an array (without "one" :) or an object ([ in front of outer_array should be {) in the first case your code is fine and in second case you can use a Map (like HashMap) and if you need to convert that map to array you can use something like this
data class MyClass(
#SerializedName("outer_array") var data: HashMap<String, List<Data>>
){
val dataAsArray get() = data.map{ it.value }
}
But if you have to parse this invalid data (e.g you cannot ask the provider/backend to fix this) even a TypeAdapter cannot help, because the JSON is invalid and you'll face an exception, you can use a regex to replace invalid [ ] with { } (but this is so dumb, try to convince the backend dev to fix the json :D)
I suggest you to use Gson, a Google service to parse JSON strings into Objects and viceversa. Simple add "implementation 'com.google.code.gson:gson:2.8.6'" into dependencies in the app's Gradle file in your Android project.
You could use two methods: toJson(Object obj) and fromJson(string json, Class<>)
if you have a class called 'MyObject', you have to put as second parameter of 'fromJson': MyObject.class, or MyObject[].class if an array.
GitHub: https://github.com/google/gson

How to handle such a json properly?

The problem is next.
In response I have JSON like
{
object: {
// a lot of different fields
}
}
I use Retrofit with gson parser. What I really need is just this object. I don't want to create class for response with the only one field. All responses server send in a such manner. As far I understand somewhere I need place simple code for fetching that one object and then use default parser for it.
Probably sorry for stupid question. I used Volley and there was quite a different approach.
Instead of creating a special class to handle this (and another special class for every other server response), just use Map<String, YourRealObjectType>. Then use this method to extract the YourRealObjectType instance for each response:
public static <T> T getFirstValue(Map<String, T> map) {
return map.values().iterator().next();
}
you can convert class into JsonObject class. then cal iterate all the elements in it one by one
#Get
ObservablegetData();
Note : use JsonObject not JSONObject

Retrofit to parse json with an indefinite number of object names

I'm using retrofit to handle rest-api calls.
I have a rest API that returns the following json
"MyObject": {
"43508": {
"field1": 4339,
"field2": "val",
"field3": 15,
"field4": 586.78
},
"1010030": {
"field1": 1339,
"field2": "val212",
"field3": 1,
"field4": 86.78
},...
}
Please notice that the object MyObject contains objects with a name that is actually an id.
For all the other rest APIs I'm using retrofit without problems.
In this case it seems not possible to use the standard approach: defining a class containing the fields expected in the response.
Is there a way to transform this json into a json containing an array of
{
"field1": xxx,
"field2": "yyy",
"field3": www,
"field4": zzz
}
Or is there a better way to deal with this problem without going back to "manually" parsing the json?
Try to use next approach:
public class Response {
Map<String, YourObject> MyObject;
// getter, setter
}
public interface GitHubService {
#GET("some_path")
Call<Response> listMyObjects();
}
All you objects will be parsed to Map. You can get the list of all ids via keySet() method or list all entries with entrySet().
Try putting the annotation, SerializedName(nameOfField) over the variable name.
#SerializedName("13445345")
MyObject object;
Well my idea is quite manual, but it should work. I will not copy and paste another person's answer here, so take a look at this answer to see how to loop through all of the myObject's keys. Then for each of those keys, make a new JSONArray and add key value pair of fieldX-valueX.
This is just a basic idea, since I think you can handle the code yourself, you seem like a guy who knows his way around the simple stuff.

How to use gson parse hetorogeneous map

I want to parse a sample json by gson like this:
"category": {
"sub_category_1": [...],
"sub_category_2": [...],
"sub_category_3": {...}
}
Because all sub categories are dynamic and I'm only interested in sub categories which are maps. So I'm using Map<String, Map<String, String>> as data type in POJO. However, I got error saying array is expected. My guess is that it's also using that data type to parse when sub category is an array. Any suggestion? I'm new to Retrofit and Gson, so it would be great if you can paste your deserializer code here.
You have to use a custom TypeAdapter for Gson. It's a hard and messy job, but it's the only way I found to make the parsing viable. Take a look at these:
http://www.javacreed.com/gson-typeadapter-example/
http://www.studytrails.com/java/json/java-google-json-type-adapter.jsp
https://google-gson.googlecode.com/svn/trunk/gson/docs/javadocs/com/google/gson/TypeAdapter.html

Getting Data from REST Api

I have a node.js application with an Angular Webfrontend and an Android App connecting to the backend via rest. Now I'm not sure how to return objects from node to the clients:
Way 1:
res.send(req.user);
If I'll do it like this I can load the user object from angular like:
return $http.post('/api/login', user)
.success(function (data) {
$rootScope.currentUser = data;
and in my Android app (simplyfied call, I use Spring and Gson):
user = SpringRestTemplate.getRestTemplate().postForObject(Routes.getLogin(), user, User.class);
This works fine, but what I actually would like to return from node would be my
Way 2:
res.send({user: req.user });
for angular everything pretty much stays the same:
$rootScope.currentUser = data.user;
but for android I didn't found any other way than to write the json conversion myself:
ResponseEntity<Map> map = SpringRestTemplate.getRestTemplate().postForEntity(Routes.getLogin(), dUser, Map.class);
LinkedTreeMap<String, User> map2 = (LinkedTreeMap<String, User>) map.getBody();
Map ltm = (Map) map.get("user");
String id = (String) ltm.get("_id");
String userName = (String) ltm.get("userName");
// ... and so on
I get, why this is the case (except why I had to use a second map), but I wonder if there's any simplier way to achieve my second way.
I left the Models and the SpringRestTemplate Code out, since this is more a general question on how to handle JSON with the following structure:
{ user : { _id: "1", userName: "foo", ...}}
Any guidance on how to deal with this problem would be much appreciated!
This is simplified, but this will solve your issue.
Create a POJO that matches the JSON Schema using Json Schema to POJO
If you can, use Retrofit or something more easily suited for Android to consume the API.
Create a retrofit service (look at the retrofit link above) and then connect to the API and you'll have a User object.
Option B:
Use GSON to deserialize your object into a POJO from your spring stuff. But I recommend using Retrofit or something similar as its easier and cleaner.

Categories

Resources