3

I am trying to use kotlinx @Serializable and Ive faced this issue:

I have the following classes:

@Serializable
sealed class GrandParent

a second one:

@Serializable
sealed class Parent() : GrandParent() {
       abstract val id: String
    }

and a third one

@Serializable
data class Child(
   override val id: String, ....
): Parent()

I'm needing of grandparent since I use it as a generic type in another class, which happen to also have a reference to the GrandParent class

@Serializable
data class MyContent(
   override val id: String,
   ....
   val data: GrandParent, <- so it has a self reference to hold nested levels
...): Parent()

Every time I try to run this I get an error...

Class 'MyContent' is not registered for polymorphic serialization in the scope of 'GrandParent'.
Mark the base class as 'sealed' or register the serializer explicitly.

I am using ktor as wrapper, kotlin 1.5.10. I did this based on https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#registered-subclasses

Any ideas?

jpganz18
  • 5,508
  • 17
  • 66
  • 115

1 Answers1

1

You should serialize and deserialize using your sealed class in order for kotlin serialization to "know" to add a discriminator with the right implementation. By default it search for type in the json but you can change it with JsonBuilder:

Json {
  classDiscriminator = "class"
}

Here is an example:

@Serializable
sealed class GrandParent

@Serializable
sealed class Parent : GrandParent() {
  abstract val id: String,
}

@Serializable
data class Child(
  override val id: String,
): Parent()

@Serializable
data class MyContent(
  override val id: String,
  val data: GrandParent,
): Parent()

fun main() {
  val test = MyContent(id = "test", data = Child(id = "child"))

  val jsonStr = Json.encodeToString(GrandParent.serializer(), test)
  println("Json string: $jsonStr")

  val decoded = Json.decodeFromString(GrandParent.serializer(), jsonStr)
  println("Decoded object: $decoded")
}

Result in console:

Json string: {"type":"MyContent","id":"test","data":{"type":"Child","id":"child"}}
Decoded object: MyContent(id=test, data=Child(id=child))

encode and decode can also be written like this (but behind the scenes it will use reflections):

val jsonStr = Json.encodeToString<GrandParent>(test)
println("Json string: $jsonStr")

val decoded = Json.decodeFromString<GrandParent>(jsonStr)
println("Decoded object: $decoded")
Tom
  • 3,711
  • 2
  • 25
  • 31