What is the correct way to make something like thi...
# serialization
b
What is the correct way to make something like this work? I guess that I need a custom
SerializersModule
?
Copy code
@Serializable
sealed class GenericInterface<T>

@Serializable
data class Generic<T>(val value: T) : GenericInterface<T>()

@Serializable
data class OtherClass(val value: GenericInterface<String>)

class SerializeTest {
    @Test
    fun test() {
        val value = OtherClass(Generic("test"))

        val encoded = Json.encodeToString(OtherClass.serializer(), value)

        val decoded = Json.decodeFromString(OtherClass.serializer(), encoded)

        assertEquals(value, decoded)
    }
}
a
hi, it's not clear to me what's wrong. Could you say more about what you expect vs what actually happens?
b
Hi 🙂 I expect the test to succeed, meaning I can encode and decode
value
and get the same result back. But I get:
Copy code
Class 'String' is not registered for polymorphic serialization in the scope of 'Any'.
To be registered automatically, class 'String' has to be '@Serializable', and the base class 'Any' has to be sealed and '@Serializable'.
Now I tried adding:
Copy code
private val module = SerializersModule {
    polymorphic(Any::class) {
        subclass(String::class)
    }
}

val json = Json { serializersModule = module }
And I get now:
Copy code
Serializer for String of kind STRING cannot be serialized polymorphically with class discriminator.
Does that mean it just works likes this for non-primitive types?
a
hmmm okay, tricky. I always find open polymorphism/generics difficult with KxS so I try to avoid it! I think making
GenericInterface<T>
sealed is not very useful for KxS. On one hand, 'sealed' means 'closed polymorphism. But because it's generic, it's actually 'open polymorphic', since there are multiple implementations. But that's just an observation, and shouldn't specifically cause your issue. What I think is more of a cause is that you're using
GenericInterface<String>
as a property. The error message you get isn't very useful, but as I think you guessed you need to write a SerializersModule to tell KxS how to serialize an abstract class. There's a section in the docs on open polymorphism and generic classes, so I think you should try something like this:
Copy code
val responseModule = SerializersModule {
    polymorphic(GenericInterface
::class) {
        subclass(Generic
.serializer(PolymorphicSerializer(Any::class)))
    }
}
personally I try and avoid the generics as much as possible in KxS classes, and instead convert
GenericInterface
to be a sealed interface with concrete implementations that have to be used in the data classes:
Copy code
@Serializable
sealed interface GenericInterface<T> {
  val value: T
}

@Serializable
data class StringData(override val value: String) : GenericInterface<String>

//@Serializable
//data class Generic<T>(val value: T) : GenericInterface<T>()

@Serializable
data class OtherClass(val value: StringData)
That approach won't fit every situation, and it's more verbose, but it's also a lot more explicit and easy to reason about. And you don't need to fiddle about with maintaining a SerializersModule.