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.
Related
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
Is it possible to convert this :
{
"news": [
{
"nid": 1,
"type": "test1",
},
{
"nid": 2,
"type": "test2",
}
]
}
To this when I receive the result with Retrofit ?
{
"nid": 1,
"type": "test1",
},
{
"nid": 2,
"type": "test2",
}
Actually in my model, I have a NewsList object with a List of News inside (with value inside : nid, type).
And the Retrofit query looks like this :
#GET("test/test.json")
suspend fun getListNews(): Response<Newslist>
Everything is working fine, but now I need to save that data in a database with Room and it will be easier if the Retrofit query was like this :
#GET("test/test.json")
suspend fun getListNews(): Response<List<News>
What is the best way to achieve this ?
Convert Json response to get something like above ?
Keep things like this and Convert the NewsList to be able to be stored in the Room Database ?
The best way is to use RXJava or Coroutine of Kotlin.
In RXJava you can use map operator to transform your response object to list of objects.
Please refer below link to get some idea of using RXJava with retrofit and transforming results:
https://www.journaldev.com/20433/android-rxjava-retrofit
The best way is to have your backend server return the canonical json data.
If this is not possible, you can customize the converterFactor by addConverterFactory method,compatible with this data format in a custom converterFactor.
first, you must to have one layer for data provider to provide for consumer (activity, VM, fragment, etc).
in this layer, you have use case to define data collection from Cloud(Retrofit) or from Local Storage(Room). more info, i have sample here
Yes, you can, using data classes, you just need to create a data class called news with a list of items with "nid" and "type", in that cases I use a plugging of Android Studio Called Kotlin data class File from JSON
I think the better way is to return NewsList object, because it makes you easier to compatible with new feature update, like adding more fields to NewsList object. You can create a new layer for saving your data to room database, and convert the raw data to List for saving it.
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
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
From Retrofit's documentation:
RESPONSE OBJECT TYPE
HTTP responses are automatically converted to a specified type using the RestAdapter's converter which defaults to JSON. The desired type is declared as the method return type or using the Callback or Observable.
#GET("/users/list")
List<User> userList();
Retrofit will automatically convert a returned response (JSON, XML, etc) into the desired object that is specified by the method.
In the documentation's case, it will return a List of User objects.
How can one interface retrofit with a badly designed API that returns a list of different typed objects.
The psudocode will look something like:
#GET("/users/list")
List<User|Pet> userList();
Edit: The API returns a mix of the two types together. There are no flags or switches to change that.
Example /users/list:
{
"data": [
{
"animal": "dog",
"speak": "woof"
},
{
"username": "Bob",
"age": 30
}
]
}