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

如何使用Moshi与Retrofit2解析数组包裹的JSON响应

问题根源

报错Expected BEGIN_OBJECT but was BEGIN_ARRAY at path $的原因非常明确:你调用的OpenWeather地理编码接口根结构是JSON数组,但你在Retrofit接口中声明的返回类型是单个LocationWeather对象,Moshi尝试将数组解析为单个实体类时类型不匹配,直接抛出解析失败异常。


解决方案

你可以根据自己的代码习惯选以下任意一种方案,都能正常解析数据。

方案1:直接修改返回类型为列表(无额外代码,推荐)

这是最符合接口实际结构的写法,不需要自定义适配器,只需要修改两处代码即可:

  1. 修改WeatherAPIService接口的返回值类型,把单个对象改成列表类型
interface WeatherAPIService {
    // 注意删掉@GET路径里多余的?,Retrofit会自动拼接查询参数的分隔符,写多余的?会导致请求路径异常
    @GET("direct")
    fun getWeatherFromAPI(@Query("q") loc: String,
                          @Query("limit") lim: Int,
                          @Query("appid") key: String): Call<List<LocationWeather>>
}
  1. 同步修改OverviewViewModel中网络请求回调的泛型,取列表第一个元素使用即可
fun getWeather(location: String) {
    WeatherApi.retrofitService.getWeatherFromAPI(location, 1, "API_KEY_REDACTED").enqueue(
        object: Callback<List<LocationWeather>> {
            override fun onResponse(call: Call<List<LocationWeather>>, response: Response<List<LocationWeather>>) {
                // 增加空列表容错,避免接口返回空数组时空指针
                _response.value = response.body()?.firstOrNull()?.name ?: "未查询到对应位置"
            }

            override fun onFailure(call: Call<List<LocationWeather>>, t: Throwable) {
                _response.value = "Failure: " + t.message
            }
        }
    )
}

方案2:自定义Moshi适配器,自动提取数组首元素

如果你不想在业务层处理列表逻辑,希望Retrofit直接返回单个LocationWeather对象,可以写一个自定义类型适配器让Moshi自动完成数组到单对象的转换:

  1. 新建LocationWeatherAdapter类,定义序列化/反序列化规则
import com.squareup.moshi.FromJson
import com.squareup.moshi.ToJson

class LocationWeatherAdapter {
    @FromJson
    fun fromJson(locationList: List<LocationWeather>): LocationWeather? {
        // 因为你传了limit=1,接口只会返回1条数据,直接取首元素即可
        return locationList.firstOrNull()
    }

    @ToJson
    fun toJson(location: LocationWeather): List<LocationWeather> {
        return listOf(location)
    }
}
  1. 把自定义适配器注册到Moshi实例中
private val moshi = Moshi.Builder()
    .add(LocationWeatherAdapter()) // 自定义适配器要放在Kotlin反射适配器之前注册
    .add(KotlinJsonAdapterFactory())
    .build()

修改完成后不需要改动原有接口和ViewModel的泛型声明,Moshi会自动完成类型转换。


内容的提问来源于stack exchange,提问作者C-h me

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 01:51:56