I have a simple hierarchy containing of the following:
They should all work with Kotlinx Serialization. It went fine until I added the Backpack
class.
I use version 1.4.32
of Kotlinx Serialization.
Here is my class hierarchy in detail
// Items.kt
@Serializable
sealed class BaseItem {
abstract val id: String
abstract val type: ItemType
abstract var brand: String
abstract var model: String
abstract var imageLink: String
abstract var traits: MutableList<Trait>
abstract var implicitTraits: MutableList<Trait>
abstract var details: MutableMap<String, String>
}
@Serializable
open class Item(
override val id: String = UUID.randomUUID().toString(),
override val type: ItemType = ItemType.UNDEFINED,
override var brand: String,
override var model: String,
override var imageLink: String,
override var traits: MutableList<Trait>,
override var implicitTraits: MutableList<Trait>,
override var details: MutableMap<String, String>,
) : BaseItem()
@Serializable // is marked as incorrect
class Backpack(
brand: String,
model: String,
imageLink: String,
traits: MutableList<Trait>,
implicitTraits: MutableList<Trait>,
details: MutableMap<String, String>,
var volume: Int
) : Item(
type = ItemType.BACKPACK,
brand = brand,
model = model,
imageLink = imageLink,
traits = traits,
implicitTraits = implicitTraits,
details = details
)
The IDE is showing me the following message for the @Serialization
annotation attached to the Backpack
class.
This class is not serializable automatically because it has primary constructor parameters that are not properties
I was not able to find out what the working way it is to make this correct
It's because the parameters of your constructor are not defined as properties of the class. To have the parameters defined as properties you have to add val
or var
to the parameters. This would resolve the error message you currently have:
@Serializable
class Backpack(
override var brand: String,
override var model: String,
override var imageLink: String,
override var traits: MutableList<Trait>,
override var implicitTraits: MutableList<Trait>,
override var details: MutableMap<String, String>,
var volume: Int
) : Item(
type = ItemType.BACKPACK,
brand = brand,
model = model,
imageLink = imageLink,
traits = traits,
implicitTraits = implicitTraits,
details = details
)
But this would still not compile as you would end up with Serializable class has duplicate serial name of property 'brand', either in the class itself or its supertypes
for all properties that are used in both classes. But anyhow, I am a little bit surprised by the design as it usually not a good practice to inherit from a non abstract class. Without knowing the intentions I am wondering whether sth.like this would not work for you as well:
sealed class BaseItem {
abstract val id: String
abstract val type: ItemType
abstract var brand: String
abstract var model: String
abstract var imageLink: String
abstract var traits: MutableList<Trait>
abstract var implicitTraits: MutableList<Trait>
abstract var details: MutableMap<String, String>
}
@Serializable
data class Item(
override val id: String = UUID.randomUUID().toString(),
override val type: ItemType = ItemType.UNDEFINED,
override var brand: String,
override var model: String,
override var imageLink: String,
override var traits: MutableList<Trait>,
override var implicitTraits: MutableList<Trait>,
override var details: MutableMap<String, String>,
) : BaseItem()
@Serializable
data class Backpack(
override val id: String = UUID.randomUUID().toString(),
override val type: ItemType = ItemType.BACKPACK,
override var brand: String,
override var model: String,
override var imageLink: String,
override var traits: MutableList<Trait>,
override var implicitTraits: MutableList<Trait>,
override var details: MutableMap<String, String>,
override var var volume: Int
) : BaseItem()
I btw. removed the @Serializable
from BaseItem
as it is unnecessary because the class is abstract anyhow and therefore won't be serialized at all. I also made your classes data class
because my impression was that those basically exist to hold data and those are usually implemented with data class
. I left the many var
I see as I don't know the reasoning for those but a small hint from my side is that you should prefer val
over var
especially in data class
. A var
in this scenario feels like a code smell to me and you might want to consider using val
instead. A good literatur for such things is the Kotlin page itself: https://kotlinlang.org/docs/coding-conventions.html#idiomatic-use-of-language-features