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

实现密封接口的值类的Kotlin序列化问题求助

问题分析

出现这个错误的核心原因是JVM inline value类的默认序列化行为与密封接口的多态序列化逻辑冲突:

  • JVM inline value类默认会被"拆包"序列化,直接输出其底层值(比如PowerWatt(123)会被序列化为123这个JSON字面量)。
  • 密封接口的多态序列化默认期望输出包含类型标识的JSON对象(比如{"type":"PowerWatt","value":123}),反序列化时会尝试解析这种结构,因此当它收到字面量时就会抛出格式不匹配的错误。
解决方案

有两种可行的解决方式,可根据你的序列化格式需求选择:

方案一:启用数组式多态序列化

修改Json配置,使用数组格式来存储多态类型信息和value类的底层值,这样既能保留value类的拆包特性,又能让多态序列化正常工作。

修改测试代码如下:

@Test
fun `Correctly serialize and deserialize a value class that implements a sealed interface`() {
    val json = Json {
        useArrayPolymorphism = true // 启用数组式多态
    }
    val power: Power = PowerWatt(123)
    val powerSerialized = json.encodeToString(power)
    // 序列化结果为 ["PowerWatt", 123]
    val powerDeserialized = json.decodeFromString<Power>(powerSerialized)
    assertEquals(power, powerDeserialized)
}

方案二:让value类以对象形式序列化

如果需要保持JSON对象格式的序列化结果,可以给每个value类自定义序列化器,强制其以对象形式输出,同时保留多态类型标识。

修改实体类代码如下:

@Serializable
sealed interface Power {
    val value: Int
}

// 给PowerWatt自定义序列化器,强制以对象形式序列化
@Serializable(with = PowerWattSerializer::class)
@JvmInline
value class PowerWatt(override val value: Int) : Power

object PowerWattSerializer : KSerializer<PowerWatt> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("PowerWatt") {
        element<Int>("value")
    }

    override fun serialize(encoder: Encoder, value: PowerWatt) {
        encoder.encodeStructure(descriptor) {
            encodeIntElement(descriptor, 0, value.value)
        }
    }

    override fun deserialize(decoder: Decoder): PowerWatt {
        return decoder.decodeStructure(descriptor) {
            PowerWatt(decodeIntElement(descriptor, 0))
        }
    }
}

// 给PowerHp做同样的处理
@Serializable(with = PowerHpSerializer::class)
@JvmInline
value class PowerHp(override val value: Int) : Power

object PowerHpSerializer : KSerializer<PowerHp> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("PowerHp") {
        element<Int>("value")
    }

    override fun serialize(encoder: Encoder, value: PowerHp) {
        encoder.encodeStructure(descriptor) {
            encodeIntElement(descriptor, 0, value.value)
        }
    }

    override fun deserialize(decoder: Decoder): PowerHp {
        return decoder.decodeStructure(descriptor) {
            PowerHp(decodeIntElement(descriptor, 0))
        }
    }
}

此时测试代码无需修改,序列化结果会是{"type":"PowerWatt","value":123},反序列化可正常完成。

适用场景
  • 方案一更适合追求简洁JSON格式、不需要对外暴露对象结构的场景。
  • 方案二更适合需要符合标准对象格式、便于外部系统解析的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 15:30:51