How to serialize kotlin sealed class with open val using kotlinx serialization
Asked Answered
H

2

12
import kotlinx.serialization.Serializable

@Serializable
sealed class Exercise(open val id: String) {

    @Serializable
    data class Theory(override val id: String) : Exercise(id)
}

I have such kind of sealed class in my code, and compiler says me: Serializable class has duplicate serial name of property 'id', either in the class itself or its supertypes.

Is there way to have open val in serializable sealed class, which works correctly when overriding it?

Hypomania answered 30/7, 2020 at 12:22 Comment(2)
Have you tried: data class Theory(id: String):Exercise(id) ?Tamartamara
I can't do so with data classes (because constructor must have only property (val / var) parameters), but even if I do it with class Theory(id: String) : Exercise(id) instead, I have this error: This class is not serializable automatically because it has primary constructor parameters that are not propertiesHypomania
L
15

This is Kotlin issue KT-38958. It seems to be a corner case of the Constructor properties requirement.

It can be solved by using the following implementation,

import kotlinx.serialization.*
import kotlinx.serialization.json.Json

@Serializable
sealed class Exercise {
    abstract val id: String

    @Serializable
    data class Theory(override val id: String) : Exercise()
}

fun main() {
    val t1 = Exercise.Theory("t1")
    val t1Json = Json.encodeToString(t1)
    println(t1Json)
    println(Json.decodeFromString<Exercise.Theory>(t1Json).toString())
}

which will output:

{"id":"t1"}
Theory(id=t1)

For details, see "Designing serializable hierarchy" in the Kotlin Serialization Guide.

Lucy answered 22/8, 2020 at 17:35 Comment(5)
In my case I cannot declare the val as abstract for some reasonEctoplasm
With a class declared as abstract (or sealed, being a special form of abstract)?Lucy
Ah no I realized I was declaring it abstract in the primary constructor instead of the class body. Typical Kotlin mistake I do :DEctoplasm
Kudos to your details link :D, thanks. It is a must readImpious
This way you're not able to use @SerialName("custom") in the abstract valAngloamerican
C
1

Though abstract property is a great way to deal with this, you can also take advantage of sealed interface which helps solving this in a more concise way

import kotlinx.serialization.Serializable

@Serializable
sealed interface Exercise {
   val id: String

   @Serializable
   data class Theory(override val id: String) : Exercise
}
Coagulum answered 28/12, 2023 at 5:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.