A Kotlin Serializer for Generic Custom Lists

Viewed 32

In many Kotlin projects, I am using the NonEmptyList from arrow-kt for improved type safety. A problem arises when I want to serialize and deserialize such lists.

My attempt at a generic custom serializer with delegation does not seem to work:

class NonEmptyListSerializer<T: Serializable>() : KSerializer<NonEmptyList<T>> {
    private val delegatedSerializer = ListSerializer(T::class.serializer())

    @OptIn(ExperimentalSerializationApi::class)
    override val descriptor = SerialDescriptor("NonEmptyList", delegatedSerializer.descriptor)

    override fun serialize(encoder: Encoder, value: NonEmptyList<T>) {
        val l = value.toList()
        encoder.encodeSerializableValue(delegatedSerializer, l)
    }

    override fun deserialize(decoder: Decoder): NonEmptyList<T> {
        val l = decoder.decodeSerializableValue(delegatedSerializer)
        return NonEmptyList.fromListUnsafe(l)
    }
}

The problem is that I cannot create the delegatedSerializer from the type parameter T, because type information is erased. Reification does not work for classes. Is there any possibility to access the serializer of the list's base object?

1 Answers

You can use a companion object with an inline operator fun invoke to create a pseudo-constructor with a reified type parameter.

class NonEmptyListSerializer<T : Serializable>(
    val delegatedSerializer: SerialDescriptor
) : KSerializer<NonEmptyList<T>> {

    companion object {
       inline operator fun <reified T> invoke() = NonEmptyListSerializer(ListSerializer(T::class.serializer()))
    }
    
    override val descriptor = SerialDescriptor("NonEmptyList", delegatedSerializer.descriptor)

    override fun serialize(encoder: Encoder, value: NonEmptyList<T>) {
        val l = value.toList()
        encoder.encodeSerializableValue(delegatedSerializer, l)
    }

    override fun deserialize(decoder: Decoder): NonEmptyList<T> {
        val l = decoder.decodeSerializableValue(delegatedSerializer)
        return NonEmptyList.fromListUnsafe(l)
    }

}
Related