Retrofit2 Using #QueryMap to Map Objects - android

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);
}
}
}
}

Related

Json parsing using Gson and Retrofit (Custom Deserializer)

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.

Retrofit POJO is null but JSON is valid

Have been scratching my head on this one. The JSON Response is a valid one:
{
"MRData": {
"xmlns": "http://ergast.com/mrd/1.4",
"series": "f1",
"url": "http://ergast.com/api/f1/current/2.json",
"limit": "30",
"offset": "0",
"total": "1",
"RaceTable": {
"season": "2014",
"round": "2",
"Races": [
{
"season": "2014",
"round": "2",
"url": "https://en.wikipedia.org/wiki/2014_Malaysian_Grand_Prix",
"raceName": "Malaysian Grand Prix",
"Circuit": {
"circuitId": "sepang",
"url": "http://en.wikipedia.org/wiki/Sepang_International_Circuit",
"circuitName": "Sepang International Circuit",
"Location": {
"lat": "2.76083",
"long": "101.738",
"locality": "Kuala Lumpur",
"country": "Malaysia"
}
},
"date": "2014-03-30",
"time": "08:00:00Z"
}
]
}
}
}
The POJO for the response:
public class ApiResponse {
MRData mrdata;
public class MRData {
String xmlns;
String series;
String url;
String limit;
String offset;
String total;
RaceTable raceTable;
}
}
The apiResponse object is always null. Anyone can point out what is wrong with the POJO object here?
Thanks.
I see a couple of potential issues here:
You are defining a class inside another class; I have never seen it done like this. You might want to separate in two different files.
Your variable names on your POJO should match the variable names on the JSON response exactly. For example:
public class ApiResponse {MRData MRData;}
If you want your POJO's variables to be different than what the JSON sends back, you should use #SerlizedName.

Retrofit map json variable to keyword

So I'm working with retrofit with an API that has a variable called "public". How would I go about getting it to automatically map like all the other variables do.
Example:
#GET("/?filter=my_images")
void getMyImages(
#Query("client_id") String id,
#Query("api_key") String key,
Callback<ImageList> callback
);
public static class Image{
int id;
String name;
String distribution;
String slug;
// Can't do this:
boolean public;
}
public static class ImageList{
String status;
List<Image> images;
}
Example API results (json):
{
"status": "OK",
"images": [
{
"id": 1,
"name": "My first snapshot",
"distribution": "Ubuntu",
"slug": "ubuntu-12.10-x32",
"public": true
},
{
"id": 2,
"name": "Automated Backup",
"distribution": "Ubuntu"
}
]
}
Retrofit uses Gson for serialization to and from JSON.
Gson provides a #SerializedName annotation in order to change the key to which a field or method is mapped. You can use this for handling your reserved word:
#SerializedName("public")
public String isPublic;
Please look at this link, which is a neater solution if there are underscores in each key.

Is this a valid JSON?

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

Trouble retrieving data from web service using GSON

I have tried a lot of samples and tutorials about GSON and how things would work using it such as:
http://www.softwarepassion.com/android-series-parsing-json-data-with-gson/
http://androidsmith.com/2011/07/using-gson-to-parse-json-on-android/
and etc.
My problem is that I have this json value returned by my http://localhost:3000/users/1.json, which is:
{
"created_at": "2012-09-20T01:43:15Z",
"id": 1,
"name": "kevin",
"updated_at": "2012-09-20T01:43:15Z"
}
Another is in this url http://localhost:3000/users.json which has a json value
[ {
"created_at": "2012-09-20T01:43:15Z",
"id": 1,
"name": "kevin",
"updated_at": "2012-09-20T01:43:15Z"
}, {
"created_at": "2012-09-20T01:43:33Z",
"id": 2,
"name": "pineda",
"updated_at": "2012-09-20T01:43:33Z"
}, {
"created_at": "2012-09-20T01:46:08Z",
"id": 3,
"name": "raphael",
"updated_at": "2012-09-20T01:46:08Z"
}, {
"created_at": "2012-09-20T16:13:42Z",
"id": 4,
"name": null,
"updated_at": "2012-09-20T16:13:42Z"
}, {
"created_at": "2012-09-20T16:18:03Z",
"id": 5,
"name": null,
"updated_at": "2012-09-20T16:18:03Z"
}, {
"created_at": "2012-09-20T16:19:23Z",
"id": 6,
"name": null,
"updated_at": "2012-09-20T16:19:23Z"
}, {
"created_at": "2012-09-20T16:20:41Z",
"id": 7,
"name": null,
"updated_at": "2012-09-20T16:20:41Z"
}
]
I am having a bit of a hard time parsing such data and getting it for storage purposes.
In order to parse your JSON, you first need to create a class to wrap your data. In your case:
public class Item {
#SerializedName("created_at")
private String createdAt;
#SerializedName("id")
private int id;
#SerializedName("name")
private String name;
#SerializedName("updated_at")
private String updatedAt;
//getters and setters
}
Then, in order to parse your 1st JSON reponse, you just have to do:
Gson gson = new Gson();
Item item = gson.fromJson(your1stJsonString, Item.class);
Your 2nd JSON response is a bit more tricky, because it's an array. The problem is that you can't simply do:
List<Item> item = gson.fromJson(your1stJsonString, List<Item>.class); //wrong!
The previous code fails because Java can't know the class of List<Item> due to type erasure.
So you have to do it this way:
Type itemListType = new TypeToken<List<Item>>() {}.getType();
List<User> itemList = gson.fromJson(your2stJsonString, itemListType);
And that's all, once you have parsed your JSON response in your object (or list of objects) you can access all the retrieved data as usual:
String name = itemList.get(i).getName();
Note 1: Notice that I've set the types of attributes created_at and updated_at as just String, because it makes things easier. I usually do it this way, and then I parse the Date or any other confictive type. Anyway, if you want to directly parse the dates, I think you can use a Custom Deserializer following Gson's User Guide.
Note2: The use of the annotation #SerializedName is interesting to separate the name of a field in the JSON response and in your app, in order to follow Java naming conventions...

Categories

Resources