Search code examples
jsonkotlingenericsserializationjsonserializer

kotlin: nested polymorphic serialization with generics


I want to serialize Map<String, Any> and one of the values type is Pair<Int, Int>. How to register the Pair as polymorphic subclass for that?

val module = SerializersModule {
    polymorphic(Any::class) {
        subclass(Int::class, PolymorphicPrimitiveSerializer(Int.serializer()))
        subclass(String::class, PolymorphicPrimitiveSerializer(String.serializer()))
        subclass(Pair::class, PolymorphicSerializer(Pair::class))
    }
}
val format = Json { serializersModule = module }
val mm = mapOf<String, Any>()
        .plus("int-int pair") to (5 to 10))
val jsoned = format.encodeToString(mm)
val mmDecoded = format.decodeFromString(jsoned)
require(mm==mmDecoded)

should encode to json like:

[{"first": "int-int pair", 
"second":{"type": "Pair", "value": 
  {"first": {"type": Int, "value":5}, "second": {"type":Int, "value": 10}}}}]

But produce the following error:

Exception in thread "main" java.lang.ExceptionInInitializerError Caused by: java.lang.IllegalArgumentException: Serializer for Pair can't be registered as a subclass for polymorphic serialization because its kind OPEN is not concrete. To work with multiple hierarchies, register it as a base class. at kotlinx.serialization.json.internal.PolymorphismValidator.checkKind(PolymorphismValidator.kt:41) at kotlinx.serialization.json.internal.PolymorphismValidator.polymorphic(PolymorphismValidator.kt:31) at kotlinx.serialization.modules.SerialModuleImpl.dumpTo(SerializersModule.kt:189) at kotlinx.serialization.json.JsonImpl.validateConfiguration(Json.kt:358) at kotlinx.serialization.json.JsonImpl.(Json.kt:352) at kotlinx.serialization.json.JsonKt.Json(Json.kt:189) at kotlinx.serialization.json.JsonKt.Json$default(Json.kt:185) at MainKt.(Main.kt:143)


Solution

  • import kotlinx.serialization.builtins.*
    val pairAnyAnySerializer = PairSerializer(
        PolymorphicSerializer(Any::class), PolymorphicSerializer(Any::class)
        ) as <KSerializer<Pair<*,*>>>()
    

    and for every type that is defined in the serializers module for polymorphic serializatin of Any it will serialize/deserialze correct.

    val json = Json {
        serializersModule = SerializersModule {
            polymorphic(Any::class) {
                subclass(String::class, PolymorphicPrimitiveSerializer(String.serializer()))
                subclass(Int::class, PolymorphicPrimitiveSerializer(Int.serializer()))
            }
        }
    }