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
相关产品推荐
相关产品推荐

