You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Retrofit解析含动态Key的JSON?附API响应与数据类示例

How to Parse This Dynamic-Key JSON Response with Retrofit

Hey there! Let's break down how to handle this JSON response with Retrofit—your current data classes just need a small tweak to match the API's structure properly.

First, let's recap what we're working with:

Your API response:

{ 
  "country1":{ "id":"0", "name":"Jack" }, 
  "country2":{ "id":"1", "name":"Rick" } 
}

Your existing data classes:

data class Countries(@Expose var country: Map<String, Country>)
data class Country(var id: String, var name: String)

The core issue here is that your Countries class expects a top-level field named country, but the API returns dynamic top-level keys (country1, country2, etc.) that directly map to Country objects. Here are two straightforward solutions:


Solution 1: Skip the Wrapper Class, Use Map<String, Country> Directly

Since the entire JSON response is just a map of string keys to Country objects, you can simplify things by using Map<String, Country> as the return type in your Retrofit interface. No need for the Countries wrapper unless you specifically need it for your code structure.

Step 1: Update Your Retrofit Interface

interface CountryApi {
    @GET("your-api-endpoint-path")
    suspend fun getCountries(): Response<Map<String, Country>>
}

Step 2: Parse the Response

When you call the API, you can directly access and iterate over the map to work with each Country object:

val response = countryApi.getCountries()
if (response.isSuccessful) {
    val countryMap = response.body()
    countryMap?.forEach { (countryKey, country) ->
        println("Country Key: $countryKey, ID: ${country.id}, Name: ${country.name}")
        // Add your logic to process each Country here
    }
}

Solution 2: Adjust the Countries Wrapper Class

If you want to keep using the Countries class to wrap the map, you need to fix how Gson (Retrofit's default serializer) maps the JSON to your class. Since there's no single top-level field named country, we can tell Gson to capture all top-level dynamic keys directly into the map.

Updated Countries Class

data class Countries(
    @Expose
    // Empty SerializedName tells Gson to map all top-level keys to this map
    @SerializedName("")
    var country: Map<String, Country>
)

For clearer naming (since "country" as the map name is a bit ambiguous):

data class Countries(
    @Expose
    @SerializedName("")
    var countryMap: Map<String, Country>
)

Corresponding Retrofit Interface

interface CountryApi {
    @GET("your-api-endpoint-path")
    suspend fun getCountries(): Response<Countries>
}

Parse the Response

val response = countryApi.getCountries()
if (response.isSuccessful) {
    val countriesWrapper = response.body()
    countriesWrapper?.countryMap?.forEach { (countryKey, country) ->
        println("Country Key: $countryKey, ID: ${country.id}, Name: ${country.name}")
    }
}

Quick Notes to Keep in Mind

  • Make sure you have the Gson Converter Factory set up in your Retrofit instance (it's included by default, but double-check if you have a custom setup):
    val retrofit = Retrofit.Builder()
        .baseUrl("your-base-url-here")
        .addConverterFactory(GsonConverterFactory.create())
        .build()
    
  • Your Country class is already perfect—its field names (id, name) exactly match the JSON keys, so no extra SerializedName annotations are needed here.

内容的提问来源于stack exchange,提问作者Mohammad Babaei TB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 08:17:31