Retrofit Enqueue解析JSON对象时省略额外封装类的实现方法
Retrofit 跳过外层容器类直接解析内部数组方案
直接声明Call<List<Person>>会报错的核心原因是:接口返回的最外层结构是JSON对象,Gson默认会尝试将整个响应体匹配你声明的返回类型,对象和数组类型不匹配就会抛出解析异常。不需要为每个列表接口单独写无业务意义的外层容器类,有两种可直接落地的实现方式:
方案1:手动提取解析(适合少量接口场景)
如果这类外层包数组的接口数量不多,不需要做全局配置,直接让Retrofit返回原始JSON元素,手动提取目标数组即可,全程不需要额外定义People类:
- 修改接口定义,返回JsonElement类型
interface ApiInterface { @GET(value = "all_people.php") fun getAllPeople(): Call<JsonElement> }
- 在响应回调中提取目标数组,手动转换为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对象包裹单个目标数组」的接口,可以写一个通用的反序列化器,一次配置后所有这类接口都可以直接返回目标列表,不需要重复写容器类:
- 先定义通用的列表提取反序列化器
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) } } }
- 初始化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)
- 之后接口就可以直接声明返回
Call<List<Person>>,和你预期的写法完全一致,Gson会自动提取外层对象里的数组完成解析
interface ApiInterface { @GET(value = "all_people.php") fun getAllPeople(): Call<List<Person>> }
注意事项
- 你原有代码中存在拼写错误:
responce的正确拼写为response,直接运行会触发编译错误,需要修正。 - 方案2中后续新增同类型接口,只需要在Gson初始化时多注册一条对应类型和字段key的规则即可,不需要新增任何容器类。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

