kotlinx.serialization icon indicating copy to clipboard operation
kotlinx.serialization copied to clipboard

Support specifying polymorphic serializer for star-projected generic types

Open Whathecode opened this issue 4 years ago • 1 comments

What is your use-case and why do you need this feature?

I want to serialize a type which contains star-projected properties: Survey below. InputElement is registered as polymorphic in SerializersModule and all concrete types are registered as non-generic classes. E.g., Text.

interface InputElement<TData : Any> { val name: String }
data class Text( override val name: String ) : InputElement<String>

@Serializable
class Survey(
    val elements: Set<InputElement<*>>
)

The above is not possible since the compiler plugin warns on the elements property:

Serializer has not been found for type 'Any'. To use context serializer as fallback, explicitly annotate type or property with @Contextual

Note that doing either does not make the error go away. This might be a potential bug, or at least an error message which could be improved.

Interestingly, the following does works:

val elements: Set<InputElement<*>> = setOf( Text( "How do you feel?" ) )

val json = Json { serializersModule = moduleWithPolymorphicInputElement }
val serializer = SetSerializer( PolymorphicSerializer( InputElement::class ) )
val serialized = json.encodeToString( serializer, elements )
val parsed = json.decodeFromString( serializer, serialized )

assertEquals( elements, parsed )

The serializers thus seem capable of doing what I want to do, but I am unable to specify the serializer on the property because of the star-projection.

I also observed serializing Stuff below works just fine:

@Serializable
sealed class Stuff
{
    data class MoreStuff<T : Any>( val input: InputElement<T> ) : Stuff()
}

I am using Kotlin 1.4.31 and serialization 1.1.0.

Describe the solution you'd like Right now, I think the only way around this is by introducing an intermediate non-generic type which is registered for polymorphic serialization. But, as portrayed in the examples that work, this does not seem necessary.

I also tried specifying a custom serializer for star-projected types: class InputElementSerializer : KSerializer<InputElement<*>>. But, this gives the same error as above when applied to the type parameter.

Is there a reason this is not allowed, whereas it does seem to work in some cases? Maybe it doesn't really work and I haven't tested enough? I do get weird errors when upgrading to Kotlin 1.5.0 and serialization 1.2.1 which may be related to this use case.

Whathecode avatar May 18 '21 21:05 Whathecode

Solution is the same as here: https://github.com/Kotlin/kotlinx.serialization/issues/393#issuecomment-468636427 Use Any instead of *, annotate it as @Contextual/@NonSerializable

sandwwraith avatar Dec 23 '24 11:12 sandwwraith