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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 00:55:08