如何用Retrofit2处理同一响应中BEGIN_ARRAY与BEGIN_OBJECT共存问题
解决Retrofit解析不一致的data字段问题
问题核心
服务器返回的data字段类型不统一:
- 有数据时是Product对象数组(
BEGIN_ARRAY) - 无数据时是空对象(
BEGIN_OBJECT)
而数据类中data定义为ArrayList<Product>,导致Gson解析失败。
解决方案:自定义Gson TypeAdapter
通过自定义TypeAdapter,让Gson在遇到空对象时自动转换成空列表,保持类型一致性。
1. 编写Product列表的TypeAdapter
import com.google.gson.* import java.io.IOException class ProductListTypeAdapter : TypeAdapter<List<Product>>() { private val productAdapter = Gson().getAdapter(Product::class.java) @Throws(IOException::class) override fun write(out: JsonWriter, value: List<Product>?) { value?.let { out.beginArray() it.forEach { product -> productAdapter.write(out, product) } out.endArray() } ?: out.nullValue() } @Throws(IOException::class) override fun read(`in`: JsonReader): List<Product> { return when (`in`.peek()) { JsonToken.BEGIN_OBJECT -> { // 跳过空对象,返回空列表 `in`.skipValue() emptyList() } JsonToken.BEGIN_ARRAY -> { // 正常解析Product数组 val products = mutableListOf<Product>() `in`.beginArray() while (`in`.hasNext()) { products.add(productAdapter.read(`in`)) } `in`.endArray() products } else -> { // 其他异常类型返回空列表 `in`.skipValue() emptyList() } } } }
2. 配置Retrofit使用自定义Gson
在构建Retrofit实例时,注册这个TypeAdapter到Gson中:
val customGson = GsonBuilder() .registerTypeAdapter(object : TypeToken<List<Product>>() {}.type, ProductListTypeAdapter()) .create() val retrofit = Retrofit.Builder() .baseUrl("你的服务器基础URL") .addConverterFactory(GsonConverterFactory.create(customGson)) .build()
3. 调整数据类(可选优化)
将ProductList中的data字段从ArrayList<Product>改为List<Product>,更符合Kotlin的集合规范:
data class ProductList( @SerializedName("err_msg") var errMsg : String, @SerializedName("err_code") var errCode : String, @SerializedName("response_id") var responseId : String, @SerializedName("api") var api : String, @SerializedName("version") var version : String, @SerializedName("data") var data: List<Product> )
后续处理
ViewModel中的请求代码无需修改,现在无论是有数据还是无数据,response.body()?.data都会返回List<Product>(有数据时是包含Product的列表,无数据时是空列表),可以正常处理业务逻辑。
内容的提问来源于stack exchange,提问作者F.P. Lucas
相关产品推荐
相关产品推荐

