I am successfully parsing a json which looks like this, which in this particular case represents an array with 2 items:
{
"items": [
{
"id": 1,
"name": "John"
},
{
"id": 2,
"name": "Mark"
}
]
}
However, I can't figure out how to parse one like this:
{
"items": {
"1": {
"id": 1,
"name": "John"
},
"2": {
"id": 2,
"name": "Mark"
}
}
}
As you can see, the second json is pretty similar to previous one, except after items it begins with "{", which means it is an object not an array, but I need that data to treat like an array.
Is this an invalid json, or I am missing something?
EDIT:
Ok, got it. The json is valid.
Let me please reformulate the question.
What I am actually looking for is how could I get the second json into a list of items.
Somewhere in the code I am doing this:
ItemsResponse itemsResponse = JsonMarshaller.fromJson(ItemsResponse.class, response);
and the ItemsResponse class:
public class ItemsResponse {
private List<Item> items;
// getters and setters
}
but it fails with an exception which says that the beginning of the json is an object, not an array.
SOLUTION:
The correct way is to use a Map instead of the List:
public class ItemsResponse {
private Map<String, Item> items;
// getters and setters
}
Read the answer of Ahmad Dwaik 'Warlock' here
You can check your json here
Related
I need to build list of places (from response array create instance of place and finally receive list of places (place in json))?
How to parse it using Gson and Retrofit with custom deserializer?
I have following strucutre:
{
"success": true,
"error": null,
"response": [
{
"loc": {
"lat": 51.50853,
"long": -0.12574
},
"place": {
"name": "London",
"state": "",
"stateFull": "",
"country": "GB",
"countryFull": "United Kingdom",
"region": "",
"regionFull": "",
"continent": "eu",
"continentFull": "Europe"
},
"profile": {
"elevM": 21,
"elevFT": 69,
"pop": 7556900,
"tz": "Europe/London",
"tzname": "BST",
"tzoffset": 3600,
"isDST": true,
"wxzone": null,
"firezone": null,
"fips": null,
"countyid": null
}
},
.............
.............
]
}
You can use Android Studio plugin RoboPOJOGenerator. It is very easy to make model classes from data.
This answer tells how to handle List response in retrofit.
Update
I don't think it is good idea to make custom deserializer just to parse a list. When you can filter or map list after getting response. It will take upto 3-4 lines of code.
If you don't want many classes. then you can safely delete Profile.java and Loc.java in your case, Gson will parse only data that you have declared in your pojo.
Make generic response class
You can make single class for handling all response by using Java Generics. See example.
public class ApiResponse<T> {
#SerializedName("error")
#Expose
private String error;
#SerializedName("success")
#Expose
private boolean success;
#SerializedName("response")
#Expose
private T response;
// make getter setters
}
Now in ApiService you can define response type. Like this
#GET("api/users/login")
Call<ApiResponse<ModelUser>> getUser();
#GET("api/users/login")
Call<ApiResponse<ModelCity>> getCity();
Thus you don't have to create 3 classes every time. This generic class will do work for all response.
I am making a two different service call. One is /genre/movie/list and the JSON looks like:
{
"genres": [
{
"id": 28,
"name": "Action"
},
{
"id": 12,
"name": "Adventure"
}
]
}
This gives me the genreId and the corresponding name. I have another endpoint discover/movie which has the following JSON.
"results": [
{
"vote_count": 263,
"id": 353081,
"video": false,
"vote_average": 7.5,
"title": "Mission: Impossible - Fallout",
"popularity": 465.786,
"poster_path": "/AkJQpZp9WoNdj7pLYSj1L0RcMMN.jpg",
"original_language": "en",
"original_title": "Mission: Impossible - Fallout",
"genre_ids": [
12,
28,
53
],
"backdrop_path": "/5qxePyMYDisLe8rJiBYX8HKEyv2.jpg",
"adult": false,
"overview": "When an IMF mission ends badly, the world is faced with dire consequences. As Ethan Hunt takes it upon himself to fulfil his original briefing, the CIA begin to question his loyalty and his motives. The IMF team find themselves in a race against time, hunted by assassins while trying to prevent a global catastrophe.",
"release_date": "2018-07-25"
},
Following are the service calls.
#GET("genre/movie/list")
Observable<HashMap<Integer, Genres>> getMovieGenres(#Query("api_key") String apiKey);
#GET("discover/movie")
Observable<MovieResponse> discoverMovies(#Query("api_key") String apiKey);
In discover_movie call, I have an array of genere_ids which gives me the id for the particular genre but it doesn't give me the name. Rather, I am making another service call with the endpoint genre/movie/list.
My question is:
How can I use Retrofit2 to Map the id to get the corresponding genre name?
Thank you!
Just to clarify, I have two Pojo:
class Movies {
int id;
List<Integer> genre_ids;
}
class MovieGenre {
int id;
String name;
}
In the above case, how do I get the genre name corresponding to the genre_ids in a Movie class. The list of genre_ids in movie class is mapped to the id in MovieGrene?
You need to add an Object not a Hashmap for the getMovieGenres Method.
Like this:
public class MovieGenres {
public List<Genres> result;
}
Asuming that Genres is like:
public class Genres {
public Integer id;
public String name;
}
And reimplement the method:
#GET("genre/movie/list")
Observable<MovieGenres> getMovieGenres(#Query("api_key") String apiKey);
For your last (edited) question, you can do something like this:
class MoviesGenreRelation {
Movie movie;
List<MovieGenre> genres = new ArrayList<>();
MoviesGenreRelation(Movie movie, List<MovieGenre> genres) {
this.movie = movie;
for(MovieGenre genre in genres) {
for(int id in movie.genre_ids) {
if (id == genre.id)
this.genres.add(genre);
}
}
}
}
iam trying to parse the json data which is shown below.i got output for other Json response by following this procedure but for this i cant get data.
{
"totalResults": 7,
"startIndex": 1,
"hasMoreResults": false,
"itemsPerPage": 10,
"results": [
{
"offering_temp_id": {
"displayName": "Cool Course",
"id": "cours000000000004841",
"course_no": "00006081",
"version": null
},
"delivery_id": {
"displayName": "Instructor-Led",
"id": "eqcat000000000000004"
},
"student_id": {
"id": "emplo000000006156648",
"displayName": "Venkat Rao",
"person_no": "VRAO"
},
"reg_no": "00008341",
"wlist_on": "2017-08-17T08:59:39.843-0400",
"wlist_priority": 5,
"Max_Count": null,
"Current_Count": null,
"is_no_show": false,
"is_walk_in": false,
"offering_action_id": {
"id": "ofapr000000000013441",
"displayName": "00009081"
},
"class_id": {
"id": "class000000000006981",
"displayName": "Cool Course"
},
"elements_to_complete": 0,
"status": "100",
"id": "regdw000000000012581"
},
// total 7 fields
],
"facets": []
}
And iam using the parser procedure as follows
public class EnrollmentParser {
public ArrayList<EnrollmentData> getData(String respnose)//EnrollmentData is my pojo class contains 4 strings and its getters and setters
{
ArrayList<EnrollmentData> dataList = new ArrayList<>();
try {
JSONObject mainObj = new JSONObject(respnose);
JSONArray array = mainObj.getJSONArray("results");
for(int i = 0;i<array.length();i++)
{
EnrollmentData data = new EnrollmentData();
JSONObject resObj = array.getJSONObject(i);
data.setReg_num(resObj.getString("reg_no"));
data.setElements_to_complete(resObj.getString("elements_to_complete"));
data.setW_list_on(resObj.getString("wlist_on"));
data.setW_list_priority(resObj.getString("wlist_priority"));
dataList.add(data);
}
} catch (Exception e) {
e.printStackTrace();
}
return dataList;
}
}
when iam trying to display the dataList returned from above method i didnt get any data i got response like
Suggest me any changes that are required to get the response
There is no faulty parsing code, everything is fine (although better use optString).
By default , the toString function will return the type '#' reference value so
You need to override toString in EnrollmentData class to see the actual content inside EnrollmentData object
You can also collect your list objects as single string using
String str = Arrays.toString(yourList.toArray());
How to print out all the elements of a List in Java?
Why don't you use Gson to convert json into object easily instead of getting the field and set them one by one?
You may check out
https://github.com/google/gson
The JSON result for getUsers I get from the server looks like this:
{
"result": [
{
"meta": {
"rows": "3"
}
},
{
"items": [
{
"id": "1",
"name": "Steve",
"age": "30"
},
{
"id": "2",
"name": "Mary",
"age": "29"
},
{
"id": "3",
"name": "Bill",
"age": "58"
}
]
}
]
}
How can I deserialize it by GSON in my android app (I'm using retrofit)?
I can't imagine any wrapper classes because of the different object types in result.
Any help would be appreciated!
For good example
Converting JSON to Java
Other way, you can convert your json to a java object
Please use org.json library http://www.json.org/java/index.html
Then, for example
json = new JSONObject(YOUR_JSON).getJSONObject("result");
JSONArray items = data.getJSONArray("items");
String name = items.getJSONObject(0).getString("name");
You can write a TypeAdapter for a type that will be (de)serialized to(from) array. You can even make it generic, so it will work with type like Pair<A, B>. Here is an example for non-generic type: https://github.com/cakoose/json-tuple-databinding-examples/blob/master/java/src/GsonEntryCustomizer.java — it (de)serializes Entry to(from array).
Disclaimer — I have not written nor tested that code, but it seems legit.
If you only encounter such problem once (like in your example), you may not bother making it generic, just write TypeAdapter for your specific pair of 2 different classes. The reading code is quite straightforward:
in.beginArray();
SomeClass1 info1 = gson.getAdapter(SomeClass1.class).read(in);
SomeClass2 info2 = gson.getAdapter(SomeClass2.class).read(in);
in.endArray();
return new SomeContainerClass(info1, info2);
(see https://github.com/cakoose/json-tuple-databinding-examples/blob/master/java/src/GsonEntryCustomizer.java#L52)
Just as the title says.. Is it possible to do this? Say I have a JSON array of something like this
{
"locations": [
{
"id": 1,
"loc": "miami"
},
{
"id": 2,
"loc": "manhattan"
},
{
"id": 3,
"loc": "lasVegas"
}
]
}
Sure, extend BaseAdapter and use the JSONArray as your backing model. However, it would be better (but by no means necessary) to convert it to a more "natural" representation in Java and use that instead.
you can convert the json to a java object. and bind the data.