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

如何用Moshi+Retrofit 2解析非零索引键的JSON列表?

解决Moshi解析非零索引键JSON对象为列表的问题

问题根源

服务器返回的log_count字段并不是JSON数组([]),而是一个以整数年份为键、数字为值的JSON对象({}),但你的数据类中把logCount定义为List<Int>?,类型不匹配导致Moshi抛出Expected BEGIN_ARRAY but was BEGIN_OBJECT错误。

解决方案

方法一:直接改用Map类型(推荐,无需额外代码)

将logCount的类型改为Map<Int, Int>?,Moshi可以直接将JSON对象解析为Kotlin的Map,完美匹配服务器返回的结构:

@Parcelize
data class UserHistory(
    @Json(name = "log_count") val logCount: Map<Int, Int>?,
    @Json(name = "days") val days: Int,
): Parcelable

后续使用时,你可以通过logCount?.keys获取年份集合,logCount?.values获取对应的数值集合,或者遍历Map处理每一组键值对。

方法二:自定义Moshi适配器转换为List(适用于必须用List的场景)

如果业务逻辑要求必须用List结构,比如需要按年份排序、绑定列表UI等,可以自定义适配器将JSON对象转换为List:

  1. 先定义一个承载年份和对应数值的数据类:
data class YearCount(val year: Int, val count: Int)
  1. 修改UserHistory中的logCount类型:
@Parcelize
data class UserHistory(
    @Json(name = "log_count") val logCount: List<YearCount>?,
    @Json(name = "days") val days: Int,
): Parcelable
  1. 编写自定义Moshi适配器:
class YearCountListAdapter {
    // 将JSON对象(Map)转换为List<YearCount>
    @FromJson
    fun fromJson(yearCountMap: Map<Int, Int>): List<YearCount> {
        return yearCountMap.map { entry -> YearCount(entry.key, entry.value) }
    }

    // 如需序列化List<YearCount>回JSON对象,实现这个方法
    @ToJson
    fun toJson(yearCountList: List<YearCount>): Map<Int, Int> {
        return yearCountList.associate { it.year to it.count }
    }
}
  1. 创建Moshi实例时添加适配器,并传入Retrofit:
// 初始化Moshi并添加自定义适配器
val moshi = Moshi.Builder()
    .add(YearCountListAdapter())
    .build()

// 配置Retrofit使用该Moshi实例
val retrofit = Retrofit.Builder()
    .baseUrl("你的基础URL")
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .build()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 04:10:12