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

如何实现Kotlinx自定义反序列化器:将T对象或List<T>转为List<T>

实现通用Kotlinx反序列化器:将单个对象或数组统一解码为列表

针对API返回JSON字段时而为单个对象、时而为数组的情况,我们可以实现一个通用的Kotlinx Serialization反序列化器,自动将两种格式统一解码为List<T>,同时也支持反向序列化(列表转单个对象或数组)。

1. 实现通用序列化器

创建SingleOrListSerializer类,它是一个通用的KSerializer,处理单个对象与数组的转换逻辑:

import kotlinx.serialization.*
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonEncoder
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject

class SingleOrListSerializer<T>(private val elementSerializer: KSerializer<T>) : KSerializer<List<T>> {
    override val descriptor: SerialDescriptor = elementSerializer.descriptor

    // 序列化逻辑:列表长度为1时转单个对象,否则转数组
    override fun serialize(encoder: Encoder, value: List<T>) {
        if (encoder is JsonEncoder) {
            when (value.size) {
                0 -> encoder.encodeJsonElement(JsonPrimitive("")) // 可根据API需求改为空数组
                1 -> encoder.encodeSerializableValue(elementSerializer, value.first())
                else -> encoder.encodeSerializableValue(ListSerializer(elementSerializer), value)
            }
        } else {
            encoder.encodeSerializableValue(ListSerializer(elementSerializer), value)
        }
    }

    // 反序列化逻辑:JSON是数组则直接解码,是对象则包装为单元素列表
    override fun deserialize(decoder: Decoder): List<T> {
        if (decoder is JsonDecoder) {
            val element = decoder.decodeJsonElement()
            return when {
                element is JsonArray -> decoder.json.decodeFromJsonElement(ListSerializer(elementSerializer), element)
                element is JsonObject -> listOf(decoder.json.decodeFromJsonElement(elementSerializer, element))
                else -> emptyList() // 处理非对象/数组的异常情况,可改为抛出异常
            }
        } else {
            return decoder.decodeSerializableValue(ListSerializer(elementSerializer))
        }
    }
}

2. 在实体类中应用序列化器

修改你的ResourceResponse类,在需要处理的字段上标注@Serializable(with = SingleOrListSerializer::class),指定使用这个通用序列化器:

@Serializable
class ResourceResponse(
    @SerialName("description")
    @Serializable(with = SingleOrListSerializer<Description>::class)
    val descriptions: List<Description>
) {
    @Serializable
    data class Description(
        @SerialName("value")
        val value: String,

        @SerialName("lang")
        val language: String,
    )
}

注:显式指定泛型参数SingleOrListSerializer<Description>可以避免自动推断可能出现的问题,确保序列化器正确关联目标类型。

3. 配置Ktor客户端的JSON序列化

在Ktor的HttpClient配置中,启用内容协商并配置Json序列化规则(如忽略未知字段、兼容输入值等):

import io.ktor.client.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json

val client = HttpClient {
    install(ContentNegotiation) {
        json(Json {
            ignoreUnknownKeys = true // 忽略API返回的未定义字段
            coerceInputValues = true // 自动处理空值、类型不匹配等情况
        })
    }
}

关键逻辑说明

  • 反序列化:通过JsonDecoder获取JSON元素,判断是数组还是对象,分别处理为列表或单元素列表。
  • 序列化:根据列表长度决定输出单个对象还是数组,适配API的格式要求。
  • 通用性:该序列化器适用于任何List<T>类型的字段,只需在对应字段上添加标注即可,无需为每个字段单独编写序列化器。

内容的提问来源于stack exchange,提问作者Moritz Großmann

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 20:46:34