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

`JsonContentPolymorphicSerializer` should provide a non-final `selectSerializer`

Open bcmedeiros opened this issue 10 months ago • 1 comments

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

JsonContentPolymorphicSerializer is a very useful abstract class and I have used it quite a few times, but now I have a slightly different problem that cannot be fixed using it.

I'm basically trying to support serialization on a Map<String, Any> where Any could be String or a a few classes that belong to a polymorphic hierarchy. (That would be a perfect case for a String | MyObject union type, but we don't have that yet).

This is how I implemented it:

class TemplateVariablesSerializer: JsonContentPolymorphicSerializer<Any>(Any::class) {
    override fun selectDeserializer(element: JsonElement): DeserializationStrategy<Any> {
        return when (element) {
            is JsonArray -> throw IllegalStateException("arrays are not supported yet")
            is JsonObject -> serializer<MySealedHierarchy>()
            is JsonPrimitive -> when {
                element.isString -> serializer<String>()
                else -> throw IllegalStateException("non-string primitive types are not supported yet")
            }
        }
    }
}

Everything works great for deserialization if I have the correct payload, but for serialization we end up with a payload without the discriminator value as the serializer of the instance is used instead of the MySealedHierarchy one.

Describe the solution you'd like

If we had something like

protected fun selectSerializer(encoder: Encoder, value: T): SerializationStrategy<T> {
    encoder.serializersModule.getPolymorphic(baseClass, value)
                    ?: value::class.serializerOrNull()
                    ?: throwSubtypeNotRegistered(value::class, baseClass)
}

we could actually override the default serializer to consider the value type:

override fun selectSerializer(encoder: Encoder, value: T): SerializationStrategy<T> {
    when (value) {
        is MySealedHierarchy -> serializer<MySealedHierarchy>()
        else -> super.selectSerializer(encoder, value)
    }
}

bcmedeiros avatar Apr 08 '24 06:04 bcmedeiros

https://github.com/Kotlin/kotlinx.serialization/issues/1247 might help with this one we have POLYMORPHIC_AND_SUBTYPES, but I think having an override-able selectSerializer is a good change anyway.

bcmedeiros avatar Apr 08 '24 06:04 bcmedeiros