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

kotlinx.serialization泛型ConcurrentLinkedQueue序列化多态错误排查

问题描述

我需要将继承自密封类A的数据类B、C序列化到java.util.ConcurrentLinkedQueue中,相关代码如下:

@Serializable
sealed class A

@Serializable
data class B(
    val x: String,
    val y: Int,
    val z: Boolean
) : A()

@Serializable
data class C(
    val a: String,
    val b: Int,
    val c: Boolean
) : A()

@Serializable
data class MyServiceCache(
    val someOtherProp: String = "Hello World",
    @Serializable(with = ConcurrentLinkedQueueSerializer::class)
    val networkCallQueue: ConcurrentLinkedQueue<A> = ConcurrentLinkedQueue(),
)

同时实现了自定义序列化器:

class ConcurrentLinkedQueueSerializer<T>(
    private val dataSerializer: KSerializer<List<T>>
) : KSerializer<ConcurrentLinkedQueue<T>> {
    override val descriptor: SerialDescriptor = dataSerializer.descriptor
    override fun serialize(encoder: Encoder, value: ConcurrentLinkedQueue<T>) = dataSerializer.serialize(
        encoder,
        value.toList()
    )
    override fun deserialize(decoder: Decoder): ConcurrentLinkedQueue<T> = ConcurrentLinkedQueue(dataSerializer.deserialize(decoder))
}

代码编译正常,但运行时序列化抛出如下错误:

Caused by: java.lang.IllegalArgumentException: Polymorphic value has not been read for class null
    at kotlinx.serialization.internal.AbstractPolymorphicSerializer.deserialize(AbstractPolymorphicSerializer.kt:67)
    at com.my.app.service.network.ConcurrentLinkedQueueSerializer.deserialize(MyServiceCache.kt:35)
    at com.my.app.service.network.ConcurrentLinkedQueueSerializer.deserialize(MyServiceCache.kt:27)
    at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
    at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableValue(AbstractDecoder.kt:43)
    at kotlinx.serialization.encoding.AbstractDecoder.decodeSerializableElement(AbstractDecoder.kt:70)
    at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableElement(StreamingJsonDecoder.kt:168)
    at com.my.app.service.network.MyServiceCache$$serializer.deserialize(MyServiceCache.kt:16)
    at com.my.app.service.network.MyServiceCache$$serializer.deserialize(MyServiceCache.kt:16)
    at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:69)
    at kotlinx.serialization.json.Json.decodeFromString(Json.kt:107)

我已参考官方文档实现自定义序列化器,但仍出现该错误,请问我的自定义序列化器存在什么问题?

问题原因与解决方案

核心问题

你的自定义序列化器没有正确处理密封类的多态序列化上下文。当直接使用KSerializer<List<T>>时,对于密封类A的子类,序列化器无法获取多态序列化所需的类型鉴别信息,导致反序列化时无法识别具体子类类型,抛出Polymorphic value has not been read错误。

修复步骤

  1. 调整序列化器的上下文传递逻辑
    自定义序列化器需要通过元素类型的序列化器来构建列表序列化器,而不是直接接收列表序列化器,这样才能携带多态类型的鉴别信息。

  2. 修改自定义序列化器实现
    重构ConcurrentLinkedQueueSerializer,让它能正确处理多态类型:

    class ConcurrentLinkedQueueSerializer<T> @SerializerConstructorMarker constructor(
        private val elementSerializer: KSerializer<T>
    ) : KSerializer<ConcurrentLinkedQueue<T>> {
        override val descriptor: SerialDescriptor =
            SerialDescriptor("ConcurrentLinkedQueue", elementSerializer.descriptor)
    
        override fun serialize(encoder: Encoder, value: ConcurrentLinkedQueue<T>) {
            val listSerializer = ListSerializer(elementSerializer)
            listSerializer.serialize(encoder, value.toList())
        }
    
        override fun deserialize(decoder: Decoder): ConcurrentLinkedQueue<T> {
            val listSerializer = ListSerializer(elementSerializer)
            return ConcurrentLinkedQueue(listSerializer.deserialize(decoder))
        }
    
        companion object : KSerializerFactory {
            @Suppress("UNCHECKED_CAST")
            override fun <T> create(type: KType, annotations: List<Annotation>): KSerializer<T>? {
                val elementType = type.arguments.first().type ?: return null
                val elementSerializer = serializer(elementType)
                return ConcurrentLinkedQueueSerializer(elementSerializer) as KSerializer<T>
            }
        }
    }
    
  3. 简化MyServiceCache的序列化注解
    无需手动指定序列化器的泛型参数,让序列化框架自动推导:

    @Serializable
    data class MyServiceCache(
        val someOtherProp: String = "Hello World",
        val networkCallQueue: ConcurrentLinkedQueue<A> = ConcurrentLinkedQueue(),
    )
    

    若需显式指定,保留@Serializable(with = ConcurrentLinkedQueueSerializer::class)即可,框架会通过KSerializerFactory自动获取正确的元素序列化器。

  4. 确认密封类的多态配置
    Kotlin Serialization会自动处理密封类的多态序列化,无需额外配置框架会自动生成类型鉴别器。如果使用JSON格式,确保Json实例的配置未禁用多态相关功能。

关键说明

  • 原序列化器直接接收KSerializer<List<T>>,但当T为密封类时,这个列表序列化器不包含多态类型的鉴别信息,反序列化时无法区分B和C。
  • 修改后的序列化器通过ListSerializer(elementSerializer)构建列表序列化器,elementSerializer是密封类A的多态序列化器,能正确处理类型鉴别逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 17:37:33