Kotlin中arrow-kt NonEmptyList通用序列化器实现难题
解决Arrow-Kt NonEmptyList的序列化/反序列化问题
核心问题分析
你的代码失败原因是泛型类型擦除:运行时无法获取泛型参数T的具体类型,因此直接调用T::class.serializer()会报错。要解决这个问题,需要显式传递或从序列化上下文中获取T的序列化器。
方案1:显式传入元素序列化器(推荐)
修改序列化器,将T的KSerializer作为构造参数传入,同时通过伴生对象的具体化方法简化调用:
import arrow.core.NonEmptyList import kotlinx.serialization.* import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder class NonEmptyListSerializer<T : Serializable>( private val elementSerializer: KSerializer<T> ) : KSerializer<NonEmptyList<T>> { private val delegatedListSerializer = ListSerializer(elementSerializer) @OptIn(ExperimentalSerializationApi::class) override val descriptor: SerialDescriptor = SerialDescriptor("NonEmptyList", delegatedListSerializer.descriptor) override fun serialize(encoder: Encoder, value: NonEmptyList<T>) { // 将NonEmptyList转为普通List后序列化 encoder.encodeSerializableValue(delegatedListSerializer, value.toList()) } override fun deserialize(decoder: Decoder): NonEmptyList<T> { val list = decoder.decodeSerializableValue(delegatedListSerializer) // 避免使用unsafe方法,空列表时抛出明确异常 return NonEmptyList.fromList(list) ?: throw SerializationException("Cannot deserialize empty list as NonEmptyList") } // 伴生对象提供具体化方法,自动获取T的序列化器 companion object { inline fun <reified T : Serializable> create(): KSerializer<NonEmptyList<T>> = NonEmptyListSerializer(T::class.serializer()) } }
使用方式
在需要序列化的类中指定序列化器:
@Serializable data class MyData( @Serializable(with = NonEmptyListSerializer::class) val items: NonEmptyList<String> ) // 或者在创建序列化器时直接调用create方法 val serializer = NonEmptyListSerializer.create<String>()
方案2:从序列化上下文获取序列化器
如果你的项目使用了上下文序列化(比如多态类型),可以从serializersModule中获取T的序列化器:
import arrow.core.NonEmptyList import kotlinx.serialization.* import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder class NonEmptyListSerializer<T : Serializable> : KSerializer<NonEmptyList<T>> { @OptIn(ExperimentalSerializationApi::class) override val descriptor: SerialDescriptor = SerialDescriptor("NonEmptyList", ListSerializer(PolymorphicSerializer(Serializable::class)).descriptor) override fun serialize(encoder: Encoder, value: NonEmptyList<T>) { val elementSerializer = encoder.serializersModule.getContextual(T::class) ?: error("No serializer registered for type ${T::class.simpleName}") encoder.encodeSerializableValue(ListSerializer(elementSerializer), value.toList()) } override fun deserialize(decoder: Decoder): NonEmptyList<T> { val elementSerializer = decoder.serializersModule.getContextual(T::class) ?: error("No serializer registered for type ${T::class.simpleName}") val list = decoder.decodeSerializableValue(ListSerializer(elementSerializer)) return NonEmptyList.fromList(list) ?: throw SerializationException("Cannot deserialize empty list as NonEmptyList") } companion object : KSerializer<NonEmptyList<*>> by NonEmptyListSerializer<Serializable>() }
注意事项
- 需要在
SerializersModule中注册T的序列化器,或为T添加@Contextual注解。 - 此方案更适合多态场景,普通场景推荐方案1。
关键优化点
- 避免使用
fromListUnsafe:该方法在列表为空时会抛出未经检查的异常,改用fromList并抛出SerializationException,符合序列化框架的异常规范。 - 显式传递序列化器:从根源解决泛型擦除问题,确保运行时能正确获取
T的序列化逻辑。
内容的提问来源于stack exchange,提问作者Ulrich Schuster
相关产品推荐
相关产品推荐

