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

Kotlin中Retrofit+Gson解析JSON报错:Expected a string but was BEGIN_ARRAY

问题:Retrofit解析JSON时抛出Expected a string but was BEGIN_ARRAY异常

问题详情

我是JSON解析新手,使用Square的Retrofit库时遇到了解析问题。

API返回的JSON响应(示例):

[{
"contactName":"ABC",
"addons":[
{"vasCode":"GTG021","propCode":"PROP0044","propValue":"0","fieldName":null},
{"vasCode":"GTG021","propCode":"PROP0043","propValue":"175000","fieldName":null},
{"vasCode":"GTG021","propCode":"PROP0018","propValue":"175000","fieldName":"COD_AMOUNT"},
{"vasCode":"GTG021","propCode":"PROP0019","propValue":"1","fieldName":"PAID_TYPE_COD"},
{"vasCode":"GTG021","propCode":"PROP0022","propValue":null,"fieldName":"BRANCH_NAME"},
{"vasCode":"GTG021","propCode":"PROP0021","propValue":null,"fieldName":"BANK_NAME"},
{"vasCode":"GTG021","propCode":"PROP0020","propValue":null,"fieldName":"ACCOUNT_NUMBER_COD"}],
"packages":"250000"
}]

定义的Kotlin数据类:

data class Contacts {
    @SerializedName("contactName") var contactName: String,
    @SerializedName("addons") var addons: ArrayList<Addons>,
    @SerializedName("packages") var packages: String,
}

data class Addons {
    @SerializedName("vasCode") var vasCode: String,
    @SerializedName("propCode") var propCode: String,
    @SerializedName("propValue") var propValue: String,
    @SerializedName("fieldName") var fieldName: String,
}

Retrofit接口:

interface GetItemData {

    @GET("searchTtNumber?mode=API")
    fun getItem(
        @Header("authorization") authorization: String,
        @Query("ttNumber") ttNumber: String,
        @Query("orgCode") orgCode: String
    ): Call<ArrayList<Contacts>>
}

Retrofit构建器:

object RetrofitHelperItem {
    fun getInstance(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://something/") // Base URL of the API
            .addConverterFactory(ScalarsConverterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
}

请求数据的类:

class ItemData(private val key : String,private val bien : String,private val orgCode : String) {
    
    // Create the ApiService instance using the Retrofit instance
    private val apiServiceItem = RetrofitHelperItem.getInstance().create(GetItemData::class.java)

    private var dt : ArrayList<Contacts>? = null
    suspend fun getItemData() : ArrayList<Contacts>? {
        try {
            val response = apiServiceItem.getItem(key, bien, orgCode).execute()
            if (response.isSuccessful) {
                dt = response.body()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return dt
    }
}

抛出的错误:

java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY at line 1 column 2075 path $[0].packages

解决方案

1. 核心问题定位

错误提示明确说明:解析$[0].packages字段时,代码期望得到字符串类型,但实际收到的是数组类型。这意味着你实际请求到的JSON中,packages字段并非示例里的字符串"250000",而是数组格式(比如["250000"])。

2. 修改数据类适配真实字段类型

将Contacts类中的packages字段类型从String改为List<String>。同时修复数据类的语法错误,并为可能为null的字段添加可空标记:

data class Contacts(
    @SerializedName("contactName") var contactName: String,
    @SerializedName("addons") var addons: ArrayList<Addons>,
    @SerializedName("packages") var packages: List<String> // 改为数组类型
)

data class Addons(
    @SerializedName("vasCode") var vasCode: String,
    @SerializedName("propCode") var propCode: String,
    @SerializedName("propValue") var propValue: String?, // 允许null
    @SerializedName("fieldName") var fieldName: String? // 允许null
)

3. 优化协程使用方式

在挂起函数中调用execute()会阻塞线程,建议改用Retrofit的挂起函数版本:

  • 修改接口为挂起函数,返回Response<List<Contacts>>(推荐用List替代ArrayList)
interface GetItemData {

    @GET("searchTtNumber?mode=API")
    suspend fun getItem(
        @Header("authorization") authorization: String,
        @Query("ttNumber") ttNumber: String,
        @Query("orgCode") orgCode: String
    ): Response<List<Contacts>>
}
  • 更新请求类逻辑:
class ItemData(private val key : String,private val bien : String,private val orgCode : String) {
    
    private val apiServiceItem = RetrofitHelperItem.getInstance().create(GetItemData::class.java)

    suspend fun getItemData() : List<Contacts>? {
        return try {
            val response = apiServiceItem.getItem(key, bien, orgCode)
            if (response.isSuccessful) {
                response.body()
            } else {
                null
            }
        } catch (e: Exception) {
            e.printStackTrace()
            null
        }
    }
}

4. 验证真实返回的JSON结构

如果不确定API返回的真实格式,可以临时通过ScalarsConverterFactory获取原始JSON字符串打印确认:

// 临时添加接口方法
interface GetItemData {
    @GET("searchTtNumber?mode=API")
    suspend fun getRawItem(
        @Header("authorization") authorization: String,
        @Query("ttNumber") ttNumber: String,
        @Query("orgCode") orgCode: String
    ): Response<String>
}

// 在请求类中调用打印
suspend fun getRawJson() {
    try {
        val response = apiServiceItem.getRawItem(key, bien, orgCode)
        if (response.isSuccessful) {
            println("Raw JSON: ${response.body()}")
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 13:10:54