如何使用 Retrofit 和 Gson 解析嵌套列表?

How to parse nested List with Retrofit and Gson?

我正在用 Gson 和 RxJava 做一个 Retrofit 教程,在某些时候它发出一个 GET 请求 returns 一个列表,我正在咨询的端点来自 TMDA 和数据我想要的嵌套在 JsonObject 中,所以我对 Gson 和 Retrofit 很陌生,所以我不知道如何以解析嵌套列表中数据的方式配置构建器,因为本教程只展示了如何直接使用 List,这是 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>>
}

这是我的建造者:

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

这是 API 响应:

{
    "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
            ]

更多电影都是这样,所以我配置它的方式收到了 JsonObject,但它需要一个列表,所以它给出了错误:

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

如果有任何关于如何解决此问题的想法,我们将不胜感激,我对这些主题还很陌生,因此提前致歉。

这是我的模型,因为有人问

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?
)

给你这样的响应模型,

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

像这样调用API,

  @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>>>
}

将您的 api 原始 json 数据解析到映射中,然后获取名为 "results" 的键。之后使用对象映射器创建 Movie POJO 对象数组


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


基本上你的响应是一个 JSON 对象,而你试图将它解析为一个 JSON 数组。您的模型 class Movie 是您得到的响应。因此,如果您想要完整的响应,请使用

fun getMovies(): Single<Movie>

这将为您提供完整的响应,其中包含 results: List<MovieData> 的值以及返回的其他参数。

希望对您有所帮助