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

Retrofit Enqueue解析JSON对象时省略额外封装类的实现方法

Retrofit 跳过外层容器类直接解析内部数组方案

直接声明Call<List<Person>>会报错的核心原因是:接口返回的最外层结构是JSON对象,Gson默认会尝试将整个响应体匹配你声明的返回类型,对象和数组类型不匹配就会抛出解析异常。不需要为每个列表接口单独写无业务意义的外层容器类,有两种可直接落地的实现方式:


方案1:手动提取解析(适合少量接口场景)

如果这类外层包数组的接口数量不多,不需要做全局配置,直接让Retrofit返回原始JSON元素,手动提取目标数组即可,全程不需要额外定义People类:

  1. 修改接口定义,返回JsonElement类型
interface ApiInterface {
    @GET(value = "all_people.php")
    fun getAllPeople(): Call<JsonElement>
}
  1. 在响应回调中提取目标数组,手动转换为Person列表
val apiService: ApiInterface = Retrofit.Builder()
    .addConverterFactory(GsonConverterFactory.create())
    .baseUrl(BASE_URL)
    .build()
    .create(ApiInterface::class.java)

apiService.getAllPeople().enqueue(object : Callback<JsonElement> {
    override fun onResponse(call: Call<JsonElement>, response: Response<JsonElement>) {
        if (!response.isSuccessful || response.body() == null) return
        // 直接取出外层对象中people字段对应的数组,转为目标列表
        val peopleList: List<Person> = Gson().fromJson(
            response.body()!!.asJsonObject.getAsJsonArray("people"),
            object : TypeToken<List<Person>>() {}.type
        )
        Log.d("First person", peopleList[0].firstName)
    }

    override fun onFailure(call: Call<JsonElement>, t: Throwable) {
        t.printStackTrace()
    }
})

方案2:自定义通用反序列化器(适合多接口复用场景)

如果项目中大量存在「外层JSON对象包裹单个目标数组」的接口,可以写一个通用的反序列化器,一次配置后所有这类接口都可以直接返回目标列表,不需要重复写容器类:

  1. 先定义通用的列表提取反序列化器
class WrapListDeserializer<T>(
    private val fieldKey: String,
    private val itemClazz: Class<T>
) : JsonDeserializer<List<T>> {
    override fun deserialize(
        json: JsonElement,
        type: Type,
        context: JsonDeserializationContext
    ): List<T> {
        val targetArray = json.asJsonObject.getAsJsonArray(fieldKey)
        return targetArray.map { context.deserialize(it, itemClazz) }
    }
}
  1. 初始化Retrofit时,给对应返回类型注册解析规则,比如针对List<Person>类型,指定提取key为people
val gson = GsonBuilder()
    .registerTypeAdapter(
        object : TypeToken<List<Person>>() {}.type,
        WrapListDeserializer("people", Person::class.java)
    )
    .create()

val apiService: ApiInterface = Retrofit.Builder()
    .addConverterFactory(GsonConverterFactory.create(gson))
    .baseUrl(BASE_URL)
    .build()
    .create(ApiInterface::class.java)
  1. 之后接口就可以直接声明返回Call<List<Person>>,和你预期的写法完全一致,Gson会自动提取外层对象里的数组完成解析
interface ApiInterface {
    @GET(value = "all_people.php")
    fun getAllPeople(): Call<List<Person>>
}

注意事项

  • 你原有代码中存在拼写错误:responce的正确拼写为response,直接运行会触发编译错误,需要修正。
  • 方案2中后续新增同类型接口,只需要在Gson初始化时多注册一条对应类型和字段key的规则即可,不需要新增任何容器类。

内容的提问来源于stack exchange,提问作者James

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 19:15:33