实现密封接口的值类的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
相关产品推荐
相关产品推荐

