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

如何用Moshi与Kotlin转换混合类型JSON数组?遇解析异常求助

解决Moshi解析混合类型JSON数组的报错问题

错误原因

你遇到的com.squareup.moshi.JsonDataException: Expected a string but was BEGIN_OBJECT at path错误,核心问题在于JSON的rows字段是混合类型的数组数组:每个子数组里既有字符串,又有嵌套对象。默认Moshi适配器无法自动处理这种固定位置的混合结构,若你在数据类里把第二个元素定义为String类型,就会和实际的对象结构产生解析冲突。

最优解决方案:自定义适配器映射强类型结构

不用@RawValue(会丢失类型安全),通过自定义Moshi适配器,把混合数组映射到强类型数据类中,既保证类型安全,又能正常解析。

步骤1:定义对应的数据类

根据JSON结构定义强类型数据类:

// 顶层响应类
data class SummaryResponse(
    val summary: Summary
)

// 包含rows的嵌套类
data class Summary(
    val rows: List<RowItem>
)

// 对应每个子数组的结构
data class RowItem(
    val date: String,
    val item: Item,
    val test1: String,
    val test2: String
)

// 子数组中的嵌套对象
data class Item(
    val name: String,
    val index: Int
)

步骤2:编写RowItem的自定义适配器

创建适配器处理数组与RowItem的双向转换:

class RowItemAdapter {
    @FromJson
    fun fromJson(reader: JsonReader): RowItem {
        reader.beginArray()
        // 按JSON子数组的顺序读取元素
        val date = reader.nextString()
        val item = reader.readValue<Item>()
        val test1 = reader.nextString()
        val test2 = reader.nextString()
        reader.endArray()
        return RowItem(date, item, test1, test2)
    }

    @ToJson
    fun toJson(writer: JsonWriter, value: RowItem) {
        // 序列化时还原为数组结构
        writer.beginArray()
        writer.value(value.date)
        writer.value(value.item)
        writer.value(value.test1)
        writer.value(value.test2)
        writer.endArray()
    }
}

步骤3:初始化Moshi并注册适配器

创建Moshi实例时添加自定义适配器,即可正常解析:

val moshi = Moshi.Builder()
    .add(RowItemAdapter())
    .build()

// 解析JSON字符串
val adapter = moshi.adapter(SummaryResponse::class.java)
val response = adapter.fromJson(yourJsonString)

备选方案:手动解析List(临时场景可用)

如果是临时需求,也可以把rows定义为List<List<Any>>,再手动转换元素类型,但这种方式丢失类型安全,不推荐长期使用:

data class SummaryResponse(
    val summary: Summary
)

data class Summary(
    val rows: List<List<Any>>
)

// 解析后手动转换类型
val response = moshi.adapter(SummaryResponse::class.java).fromJson(jsonString)
response?.summary?.rows?.forEach { row ->
    val date = row[0] as String
    val itemMap = row[1] as Map<String, Any>
    val item = Item(
        name = itemMap["name"] as String,
        index = itemMap["index"] as Int
    )
    val test1 = row[2] as String
    val test2 = row[3] as String
    // 处理业务逻辑
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 08:19:54