1

I'm doing a Retrofit tutorial with Gson and RxJava and at some point it makes a GET request that returns a List, the thing is the endpoint that I'm consulting is from TMDA and the data that I want is nested inside a JsonObject, so I'm very new to Gson and Retrofit so I don't know how to config the builder in a way that it parses the data inside the nested List, since the tutorial only shows how it works directly with a List, this is the code of the RetrofitConfig:

   @GET("/3/discover/movie?api_key=${apiKey}&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1")
   fun getMovies(): Single<List<Movie>>
}

And here is my builder:

 private val api = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .build()
        .create(MoviesApi::class.java)

    fun getMovies(): Single<List<Movie>> {
        return api.getMovies()
    }

Here is the API response:

{
    "page": 1,
    "total_results": 10000,
    "total_pages": 500,
    "results": [
        {
            "popularity": 259.91,
            "vote_count": 366,
            "video": false,
            "poster_path": "/aQvJ5WPzZgYVDrxLX4R6cLJCEaQ.jpg",
            "id": 454626,
            "adult": false,
            "backdrop_path": "/qonBhlm0UjuKX2sH7e73pnG0454.jpg",
            "original_language": "en",
            "original_title": "Sonic the Hedgehog",
            "genre_ids": [
                28,
                35,
                878,
                10751
            ],
            "title": "Sonic the Hedgehog",
            "vote_average": 7.1,
            "overview": "Based on the global blockbuster videogame franchise from Sega, Sonic the Hedgehog tells the story of the world’s speediest hedgehog as he embraces his new home on Earth. In this live-action adventure comedy, Sonic and his new best friend team up to defend the planet from the evil genius Dr. Robotnik and his plans for world domination.",
            "release_date": "2020-02-12"
        },
        {
            "popularity": 253.357,
            "vote_count": 4333,
            "video": false,
            "poster_path": "/7IiTTgloJzvGI1TAYymCfbfl3vT.jpg",
            "id": 496243,
            "adult": false,
            "backdrop_path": "/TU9NIjwzjoKPwQHoHshkFcQUCG.jpg",
            "original_language": "ko",
            "original_title": "기생충",
            "genre_ids": [
                35,
                18,
                53
            ],
            "title": "Parasite",
            "vote_average": 8.6,
            "overview": "All unemployed, Ki-taek's family takes peculiar interest in the wealthy and glamorous Parks for their livelihood until they get entangled in an unexpected incident.",
            "release_date": "2019-05-30"
        },
        {
            "popularity": 213.161,
            "vote_count": 2415,
            "video": false,
            "poster_path": "/xBHvZcjRiWyobQ9kxBhO6B2dtRI.jpg",
            "id": 419704,
            "adult": false,
            "backdrop_path": "/5BwqwxMEjeFtdknRV792Svo0K1v.jpg",
            "original_language": "en",
            "original_title": "Ad Astra",
            "genre_ids": [
                12,
                18,
                9648,
                878,
                53
            ],
            "title": "Ad Astra",
            "vote_average": 6,
            "overview": "The near future, a time when both hope and hardships drive humanity to look to the stars and beyond. While a mysterious phenomenon menaces to destroy life on planet Earth, astronaut Roy McBride undertakes a mission across the immensity of space and its many perils to uncover the truth about a lost expedition that decades before boldly faced emptiness and silence in search of the unknown.",
            "release_date": "2019-09-17"
        },
        {
            "popularity": 171.658,
            "vote_count": 776,
            "video": false,
            "poster_path": "/h4VB6m0RwcicVEZvzftYZyKXs6K.jpg",
            "id": 495764,
            "adult": false,
            "backdrop_path": "/uozb2VeD87YmhoUP1RrGWfzuCrr.jpg",
            "original_language": "en",
            "original_title": "Birds of Prey (and the Fantabulous Emancipation of One Harley Quinn)",
            "genre_ids": [
                28,
                35,
                80
            ],
            "title": "Birds of Prey (and the Fantabulous Emancipation of One Harley Quinn)",
            "vote_average": 6.8,
            "overview": "After her breakup with the Joker, Harley Quinn joins forces with singer Black Canary, assassin Huntress, and police detective Renee Montoya to help a young girl named Cassandra, who had a hit placed on her after she stole a rare diamond from crime lord Roman Sionis.",
            "release_date": "2020-02-05"
        },
        {
            "popularity": 154.297,
            "vote_count": 2258,
            "video": false,
            "poster_path": "/pThyQovXQrw2m0s9x82twj48Jq4.jpg",
            "id": 546554,
            "adult": false,
            "backdrop_path": "/cjTQSwcsfVdirSFSHNBXRGkxmWa.jpg",
            "original_language": "en",
            "original_title": "Knives Out",
            "genre_ids": [
                35,
                80,
                18,
                9648,
                53
            ]

It goes like that for more movies, so the way I have it configurated I receive the JsonObject but it is expecting a List so it gives the error:

 Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

So any ideas on how to solve this would be very appreciated, I'm very new to these topics so sorry in advance.

Here is my model since someone asked

data class Movie(
    val page: Int?,
    val total_results: Int?,
    val total_pages: Int?,
    val results: List<MovieData>
)

data class MovieData(
    val popularity: Int?,
    val vote_count: Int?,
    val video: Boolean?,
    val poster_path: String?,
    val id: Int?,
    val adult: Boolean?,
    val backdrop_path: String?,
    val original_language: String?,
    val original_title: String?,
    val genre_ids: List<Int>?,
    val title: String?,
    val vote_average: Int?,
    val overview: String?,
    val release_date: String?
)
Federico
  • 31
  • 7

3 Answers3

1

Basically your response is a JSON Object while you are trying to parse it as an JSON Array. Your Model class Movie is the response your get. So if you want the complete response use

fun getMovies(): Single<Movie>

this will give you the complete response, with the value of results: List<MovieData> within it alongwith other parameters returned.

Hope this helps you out

ljk
  • 1,496
  • 1
  • 11
  • 14
  • Thank you!! Pretty sure lots of answers here work but yours was pretty clear – Federico Feb 25 '20 at 19:58
  • When I try to do the second one it gives me an error saying: **No type arguments expected for class MovieData** any idea why? – Federico Feb 26 '20 at 00:24
  • can you try if the 2nd case is working if you use ```fun getMovies(): Single>``` if not I may have been wrong about the case and you still need it wrapped in a class. Editing the answer accordingly. – ljk Feb 26 '20 at 02:27
0

Here you Response Model like this,

data class Response(
    @SerializedName("page")
    val page: Int = 0,
    @SerializedName("results")
    val results: List<Result> = listOf(),
    @SerializedName("total_pages")
    val totalPages: Int = 0,
    @SerializedName("total_results")
    val totalResults: Int = 0
) {
    data class Result(
        @SerializedName("adult")
        val adult: Boolean = false,
        @SerializedName("backdrop_path")
        val backdropPath: String = "",
        @SerializedName("genre_ids")
        val genreIds: List<Int> = listOf(),
        @SerializedName("id")
        val id: Int = 0,
        @SerializedName("original_language")
        val originalLanguage: String = "",
        @SerializedName("original_title")
        val originalTitle: String = "",
        @SerializedName("overview")
        val overview: String = "",
        @SerializedName("popularity")
        val popularity: Double = 0.0,
        @SerializedName("poster_path")
        val posterPath: String = "",
        @SerializedName("release_date")
        val releaseDate: String = "",
        @SerializedName("title")
        val title: String = "",
        @SerializedName("video")
        val video: Boolean = false,
        @SerializedName("vote_average")
        val voteAverage: Double = 0.0,
        @SerializedName("vote_count")
        val voteCount: Int = 0
    )
}

Call API Like This,

  @GET("/3/discover/movie?api_key=${apiKey}&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1")
   fun getMovies(): Single<Response<List<Movie>>>
}
Ronak Ukani
  • 603
  • 5
  • 20
0

Parse your api raw json data into map then get key named "results". After that use object mapper to create an array of Movie POJO object


JsonParser springParser = JsonParserFactory.getJsonParser();
Map<String, Object> map = springParser.parseMap(jsonData);
objectMapper.configure(com.fasterxml.jackson.core.JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
Movie[] movies = objectMapper.readValue(map.get("results").toString().replace('=', ':'), Movie[].class);