Android Retrofit适配API返回JsonObject与JsonArray的方案问询
解决Retrofit适配API返回JsonObject/JsonArray的问题
当API有时返回单个JsonObject(对应单个实体)、有时返回JsonArray(对应实体列表)时,可通过自定义Gson反序列化器统一适配,让Retrofit始终返回List<ModelsResponseModel>:
步骤1:修改自定义反序列化器
将反序列化器的目标类型改为List<ModelsResponseModel>,根据输入的Json类型做不同处理:
- 若为JsonArray:直接解析为List
- 若为JsonObject:把单个对象包装成单元素List
private class ListModelsDeserializer : JsonDeserializer<List<ModelsResponseModel>> { @Throws(JsonParseException::class) override fun deserialize( json: JsonElement, typeOfT: Type?, context: JsonDeserializationContext ): List<ModelsResponseModel> { val modelList = mutableListOf<ModelsResponseModel>() return when { json.isJsonArray -> { // 解析数组为实体列表 json.asJsonArray.forEach { element -> val model = context.deserialize<ModelsResponseModel>(element, ModelsResponseModel::class.java) modelList.add(model) } modelList } json.isJsonObject -> { // 单个实体包装为列表 val model = context.deserialize<ModelsResponseModel>(json, ModelsResponseModel::class.java) modelList.add(model) modelList } else -> throw JsonParseException("无法识别的Json类型") } } }
步骤2:在Retrofit中注册反序列化器
构建Gson实例时注册自定义解析器,再传入Retrofit构建流程:
object ModelsServiceBuilder { private const val BASE_URL = "你的API基础地址" private fun getRetrofit(): Retrofit { val gson = GsonBuilder() // 注册List类型的反序列化器 .registerTypeAdapter( object : TypeToken<List<ModelsResponseModel>>() {}.type, ListModelsDeserializer() ) .create() return Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create(gson)) .build() } fun buildService(service: Class<ModelsApiInterface>): ModelsApiInterface { return getRetrofit().create(service) } }
步骤3:优化接口回调逻辑
接口定义保持不变(返回Call<List<ModelsResponseModel>>),但回调需增加空安全判断和错误处理:
val modelsRequestModel = ModelsRequestModel(API_KEY) val modelsResponse = ModelsServiceBuilder.buildService(ModelsApiInterface::class.java) modelsResponse.sendReq(modelsRequestModel).enqueue(object : Callback<List<ModelsResponseModel>> { override fun onResponse( call: Call<List<ModelsResponseModel>>, response: Response<List<ModelsResponseModel>> ) { if (response.isSuccessful) { val modelsList = response.body() ?: emptyList() when { modelsList.isNotEmpty() -> { val firstModel = modelsList.first() if ("error" == firstModel.status) { Toast.makeText(activity, "错误:${firstModel.message}", Toast.LENGTH_LONG).show() } else { createCarousel(modelsList) generateViewModel.updateModelsData(modelsList) } } else -> Toast.makeText(activity, "无返回数据", Toast.LENGTH_SHORT).show() } } else { Toast.makeText(activity, "请求失败:${response.code()}", Toast.LENGTH_LONG).show() } } override fun onFailure(call: Call<List<ModelsResponseModel>>, t: Throwable) { Log.e("log", "请求失败", t) Toast.makeText(activity, "请求失败:${t.message}", Toast.LENGTH_LONG).show() } })
关键提示
- 不要针对单个实体写反序列化器,需匹配接口返回的
List类型才能生效 - 复用Gson自带的
context.deserialize解析实体,避免手动解析字段导致的遗漏或错误 - 回调必须先判断
response.isSuccessful,再处理返回体,同时处理body为空的情况
内容的提问来源于stack exchange,提问作者Jan Ovesny
相关产品推荐
相关产品推荐

