Search code examples
kotlinkotlin-multiplatformkotlinx.serialization

problems with serialization of sealed interface in kotlin


Hoping you might be able to help, as I'm slowly tearing my hair out and I'm not sure where I've gone wrong, but hope it might also be obvious.


@Serializable
data class Foo
(
   var text: String?
) : Bar



@Serializable
sealed interface Bar {

    var id: String?
    val hash: String
        get() {
            return this.toString().md5()
        }
    }

   fun save(): Boolean {
        return try {
            this.id = this.hash
            val tmp = Json.encodeToString(this)
            //save the number to Kali datastore.
            Datalayer.save(tmp)
            true
        } catch (e: Exception) {
            false
        }
    }



var aa = Foo("woo")
aa.save()

The issue I have, is when i get to aa.save(), I get

kotlinx.serialization.SerializationException: Class 'Foo' is not registered for polymorphic serialization in the scope of 'Bar'.
Mark the base class as 'sealed' or register the serializer explicitly.

this is thrown from val tmp = Json.encodeToString(this)

but as far as I can tell, that's exactly what I have done? what have I done wrong? am I doing something overall stupid? I'm sort of stumped...

my kotlinx serialization is implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.0"),

but also have set kotlin("plugin.serialization") version "1.7.20"

I was sort of expecting it to work, but I got the error above. I've tried faffing with versions of things to see if that helps, but overall, no dice.


Solution

  • Like exception says the problem is that you haven't registered polymorphic serialization for your Foo class. You have several possible solutions to avoid it

    1. If you don't want to use Kotlin Serialization polymorphism then you can simply extract your save method into extension:
    public inline fun <reified T: Bar> T.save(): boolean {
        return try {
            this.id = this.hash
            val tmp = Json.encodeToString(this)
            //save the number to Kali datastore.
            Datalayer.save(tmp)
            true
        } catch (e: Exception) {
            false
        }
    }
    
    1. If you want to use Kotlin Serialization polymorphism then you can register the serializer explicitly
    val module = SerializersModule {
        polymorphic(Bar::class) {
            subclass(Foo::class)
        }
    }
    
    val format = Json { serializersModule = module }
    
    1. If you want to use Kotlin Serialization polymorphism then you can use sealed class instead of interface
    @Serializable
    sealed class Bar {
    
        abstract var id: String?
    
        val hash: String
            get() {
                return this.toString().md5()
            }
        }
    
       fun save(): Boolean {
            return try {
                this.id = this.hash
                val tmp = Json.encodeToString(this)
                //save the number to Kali datastore.
                Datalayer.save(tmp)
                true
            } catch (e: Exception) {
                false
            }
        }
    }