Balazs Toth
08/22/2023, 7:37 AMSerializersModule
?
@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)
}
}
Adam S
08/22/2023, 8:27 AMBalazs Toth
08/22/2023, 8:31 AMvalue
and get the same result back.
But I get:
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'.
Balazs Toth
08/22/2023, 8:33 AMprivate val module = SerializersModule {
polymorphic(Any::class) {
subclass(String::class)
}
}
val json = Json { serializersModule = module }
Balazs Toth
08/22/2023, 8:34 AMSerializer for String of kind STRING cannot be serialized polymorphically with class discriminator.
Balazs Toth
08/22/2023, 8:34 AMAdam S
08/22/2023, 8:42 AMGenericInterface<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:
val responseModule = SerializersModule {
polymorphic(GenericInterface
::class) {
subclass(Generic
.serializer(PolymorphicSerializer(Any::class)))
}
}
Adam S
08/22/2023, 8:46 AMGenericInterface
to be a sealed interface with concrete implementations that have to be used in the data classes:
@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.