2

Recently I've been trying to implement Kotlinx Serialization for polymorphic class hierarchy. I used this guide, however my example was a bit more complex. I had the following class structure:

@Serializable
open class Project(val name: String)

@Serializable
@SerialName("owned")
open class OwnedProject(val owner: String) : Project("kotlin")

@Serializable
@SerialName("owned_owned")
class OwnedOwnedProject(val ownerOwner: String) : OwnedProject("kotlinx.coroutines")

And was trying to deserialize OwnedOwnedProject instance using following code:

val module = SerializersModule {
    polymorphic(Project::class) {
        polymorphic(OwnedProject::class) {
            subclass(OwnedOwnedProject::class)
        }
    }
}

val format = Json { serializersModule = module }

fun main() {
    val str = "{\"type\":\"owned_owned\",\"name\":\"kotlin\",\"owner\":\"kotlinx.coroutines\",\"ownerOwner\":\"kotlinx.coroutines.launch\"}"
    val obj = format.decodeFromString(PolymorphicSerializer(Project::class), str)
    println(obj)
}

However whatever combinations of SerializersModule definition I tried, it always ended up with kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for class discriminator 'owned_owned' error.

Could you please give me a hint: how to implement SerializersModule for given class structure (deeper than two)? Am I missing something?

aSemy
  • 5,485
  • 2
  • 25
  • 51
pseusys
  • 407
  • 2
  • 5
  • 17
  • 1
    I think you would need to register `OwnedOwnedProject` directly under `Project`, but my experience with open polymorphism in Kotlinx Serialization is quite limited. – Joffrey Jul 18 '22 at 08:15
  • @Joffrey, wow, you're right! It really worked! You can add an answer for me to accept it. Never would've thought it should work that (simple!) way actually! – pseusys Jul 18 '22 at 10:34

1 Answers1

1

It seems you would need to register OwnedOwnedProject directly under Project as well, so the serializer knows it's a possible subclass:

val module = SerializersModule {
    polymorphic(Project::class) {
        subclass(OwnedOwnedProject::class)
    }
    polymorphic(OwnedProject::class) {
        subclass(OwnedOwnedProject::class)
    }
}
Joffrey
  • 32,348
  • 6
  • 68
  • 100
  • Or just `polymorphic(Project::class) { subclass(OwnedProject::class); subclass(OwnedOwnedProject::class) }` worked for me as well, looks somewhat simplier. – pseusys Jul 18 '22 at 10:43
  • I think if you do this you won't be able to deserialize `OwnedOwnedProject` if using `OwnedProject` deserializer. You might not need it, but for completness I believe it's necessary. – Joffrey Jul 18 '22 at 11:07
  • Yes, you're right. But in my case there was need for deserializing any ancestor using their common predecessor serializer. And in that case I guess it's more simple. However your solutikn is really more general. – pseusys Jul 19 '22 at 13:19