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

如何在Kotlin Serialization中反序列化外层带类型的多态JSON

解决Kotlin Serialization中字段顺序不固定的多态反序列化问题

由于JSON中type和animal字段顺序不固定,常规按索引解码的方式会失败。核心思路是先完整读取整个JSON对象,提取type后再针对性反序列化animal。

实现步骤

  1. 定义基础类型(假设AnimalType是枚举类):
enum class AnimalType { CAT, BIRD }
interface IAnimal
  1. 为Wrapper编写自定义序列化器:
object WrapperSerializer : KSerializer<Wrapper> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Wrapper") {
        element("type", AnimalType.serializer().descriptor)
        element("animal", PolymorphicSerializer(IAnimal::class).descriptor)
    }

    override fun serialize(encoder: Encoder, value: Wrapper) {
        encoder.encodeStructure(descriptor) {
            encodeSerializableElement(descriptor, 0, AnimalType.serializer(), value.type)
            encodeSerializableElement(descriptor, 1, PolymorphicSerializer(IAnimal::class), value.animal)
        }
    }

    override fun deserialize(decoder: Decoder): Wrapper {
        // 先读取完整JSON对象,不受字段顺序影响
        val jsonObject = decoder.decodeJsonElement().jsonObject
        
        // 提取并反序列化type字段
        val type = jsonObject["type"]?.let {
            Json.decodeFromJsonElement(AnimalType.serializer(), it)
        } ?: throw SerializationException("JSON缺少必填字段'type'")
        
        // 根据type反序列化对应的animal实例
        val animal = jsonObject["animal"]?.let { animalJson ->
            when(type) {
                AnimalType.CAT -> Json.decodeFromJsonElement(Cat.serializer(), animalJson)
                AnimalType.BIRD -> Json.decodeFromJsonElement(Bird.serializer(), animalJson)
            }
        } ?: throw SerializationException("JSON缺少必填字段'animal'")
        
        return Wrapper(type, animal)
    }
}
  1. 为数据类添加序列化注解:
@Serializable(with = WrapperSerializer::class)
data class Wrapper(val type: AnimalType, val animal: IAnimal)

@Serializable
data class Bird(val color: String, val canFly: Boolean): IAnimal

@Serializable
data class Cat(val color: String, val canJump: Boolean): IAnimal

测试验证

fun main() {
    val catJson = """{"type": "cat", "animal": {"color": "red", "canJump": true}}"""
    val birdJson = """{"animal": {"color": "red", "canFly": true}, "type": "bird"}"""

    val catWrapper = Json.decodeFromString<Wrapper>(catJson)
    val birdWrapper = Json.decodeFromString<Wrapper>(birdJson)

    println(catWrapper) // 输出:Wrapper(type=CAT, animal=Cat(color=red, canJump=true))
    println(birdWrapper) // 输出:Wrapper(type=BIRD, animal=Bird(color=red, canFly=true))
}

该方案通过先读取完整JSON对象的方式,彻底规避了字段顺序对解码逻辑的影响,确保无论type和animal的顺序如何,都能正确完成反序列化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 23:42:31