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

Kotlinx.Serialization配合OkHttpClient反序列化JSON始终返回Failure问题

问题根因

  • 数据类结构与JSON结构不匹配:你定义的Fact类将data字段声明为String类型,但实际接口返回的顶层data字段是数组,数组内的元素是包含fact字段的对象,而非字符串,类型不匹配导致反序列化失败。
  • 自定义序列化器逻辑错误:你给JsonTransformingSerializer传入的基类是List<String>,但数组内元素是JsonObject不是字符串,同时transformDeserialize方法中JsonArray(listOf(element)).first()的逻辑相当于把原数组又包了一层新数组再取第一个元素,最终拿到的还是原数组,没有正确提取数组内的目标对象。

实现方案

方案1:用通用响应包装类实现(推荐,逻辑简单易维护)

不需要自定义序列化器,先定义通用的接口响应结构,再提取第一个元素即可:

// 1. 定义Fact数据类,对应数组内的对象结构
@Serializable
data class Fact(
    val fact: String
) : java.io.Serializable

// 2. 定义通用接口响应包装类,ignoreUnknownKeys会自动忽略status、message等不需要的字段
@Serializable
private data class ApiResponse<T>(
    val data: List<T>
)

修改你的getFact相关逻辑:

suspend fun getFact(): Result<Fact> =
    try {
        val responseStr = client.newCall(Request.Builder().url("myURL".toHttpUrl()).build()).execute().body!!.string()
        // 先反序列化为带数组的响应结构,再取第一个元素
        val apiResponse = Json { ignoreUnknownKeys = true }.decodeFromString<ApiResponse<Fact>>(responseStr)
        Result.success(apiResponse.data.first())
    } catch (e: Exception) {
        Result.failure(e)
    }

方案2:用自定义序列化器实现(符合你原实现思路)

如果不想额外定义包装类,可以调整序列化器逻辑:

// 1. Fact类直接对应你需要的输出结构
@Serializable
data class Fact(
    val fact: String
) : java.io.Serializable

// 2. 自定义序列化器,直接从顶层JSON提取数组第一个元素作为Fact对象
object FactResponseSerializer : JsonTransformingSerializer<Fact>(Fact.serializer()) {
    override fun transformDeserialize(element: JsonElement): JsonElement {
        // 取顶层data数组的第一个元素,直接作为Fact的反序列化源
        return element.jsonObject["data"]!!.jsonArray.first()
    }
}

反序列化时指定使用该序列化器即可:

val data = Json { ignoreUnknownKeys = true }.decodeFromString<Fact>(FactResponseSerializer, responseStr)

两种方案都可以得到你需要的Fact(fact=This is random information i need to get)结果。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 21:24:02