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

Retrofit解析报错:Expected BEGIN_ARRAY but was BEGIN_OBJECT 解决方法

解决Retrofit解析动态键JSON的"Expected BEGIN_ARRAY but was BEGIN_OBJECT"错误

你遇到的错误核心是:接口返回的是带动态字符串键的JSON对象(如示例中的"2324"),但你用List<Asset>作为请求返回类型,Retrofit期望解析数组(BEGIN_ARRAY),实际却拿到了对象(BEGIN_OBJECT),导致解析失败。

接口返回的JSON结构示例

{
    "2324": {
        "Install_Date": "2013-11-28",
        "LATITUDE": 0.51664504,
        "LONGITUDE": 0.30964532,
        "Module_Count": 14
    },
    // 更多类似键值对
}

你的Asset数据类定义

data class Asset(
    @SerializedName("Install_Date")
    val assetInstallDate: String,

    @SerializedName("LATITUDE")
    val LATITUDE: Double,

    @SerializedName("LONGITUDE")
    val LONGITUDE: Double,

    @SerializedName("Module_Count")
    val assetModuleCount: Int,
)

原请求代码

call.enqueue(object : Callback<List<Asset>>{
    // 回调逻辑
})

方法一:直接用Map<String, Asset>作为返回类型

这是最简洁的方案,因为接口返回的本质就是「字符串键 -> Asset对象」的映射,Retrofit(配合Gson)可以直接解析为Map<String, Asset>:

  1. 修改Retrofit接口的返回类型:
interface ApiService {
    @GET("your-endpoint-url")
    fun getAssets(): Call<Map<String, Asset>>
}
  1. 修改请求回调逻辑:
call.enqueue(object : Callback<Map<String, Asset>>{
    override fun onResponse(call: Call<Map<String, Asset>>, response: Response<Map<String, Asset>>) {
        if (response.isSuccessful) {
            val assetMap = response.body()
            // 若需要转为List<Asset>,直接取Map的values集合
            val assetList = assetMap?.values?.toList() ?: emptyList()
            // 处理assetList
        }
    }

    override fun onFailure(call: Call<Map<String, Asset>>, t: Throwable) {
        // 处理失败逻辑
    }
})

方法二:自定义Gson TypeAdapter(直接解析为List)

如果希望直接拿到List<Asset>,可以自定义Gson的TypeAdapter,将JSON对象转换为Asset列表:

  1. 编写TypeAdapter类:
class AssetListTypeAdapter : TypeAdapter<List<Asset>>() {
    private val assetAdapter = Gson().getAdapter(Asset::class.java)

    override fun write(out: JsonWriter, value: List<Asset>) {
        // 若不需要将List<Asset>序列化为原结构,可留空或抛出异常
        throw UnsupportedOperationException("序列化逻辑未实现")
    }

    override fun read(`in`: JsonReader): List<Asset> {
        val assets = mutableListOf<Asset>()
        `in`.beginObject()
        while (`in`.hasNext()) {
            // 跳过动态键,直接解析对应的Asset对象
            `in`.nextName()
            val asset = assetAdapter.read(`in`)
            assets.add(asset)
        }
        `in`.endObject()
        return assets
    }
}
  1. 在Retrofit构建时配置该TypeAdapter:
val gson = GsonBuilder()
    .registerTypeAdapter(object : TypeToken<List<Asset>>() {}.type, AssetListTypeAdapter())
    .create()

val retrofit = Retrofit.Builder()
    .baseUrl("your-base-url")
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()
  1. 配置完成后,你可以继续使用原有的Callback<List<Asset>>请求方式,无需修改原有请求代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 16:58:43