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>:
- 修改Retrofit接口的返回类型:
interface ApiService { @GET("your-endpoint-url") fun getAssets(): Call<Map<String, Asset>> }
- 修改请求回调逻辑:
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列表:
- 编写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 } }
- 在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()
- 配置完成后,你可以继续使用原有的
Callback<List<Asset>>请求方式,无需修改原有请求代码。
内容的提问来源于stack exchange,提问作者Francisco Torres
相关产品推荐
相关产品推荐

