如何实现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
相关产品推荐
相关产品推荐

