Problem with value classes serialization/deserialization
Describe the bug
Given an interface Image its value subclass IconImage, WebImage and a data class MaskModel:
@Serializable
sealed interface Image
@JvmInline
@Serializable
value class IconImage internal constructor(val value: String) : Image
@JvmInline
@Serializable
value class WebImage(val value: String) : Image
@Serializable
data class MaskModel(
val opacity: Int,
val text: TextModel
)
When trying to convert to Json another classes that uses IconImage and MaskModel as a properties:
@Serializable
data class Sample(
val image: Image,
val mask: MaskModel
)
fun main() {
val sample = Sample(
image = IconImage("ADD"),
mask = mask(
color = Colors.alert,
opacity = 100,
text = text { }
)
)
println(Json.encodeToString(sample)) //{"image":"ADD","mask":"type":"com.package.MaskModel","opacity":100,"text":{"text":"<html></html>"}}}
}
I found two problems:
-
A "type" property is inserted in the mask object. As far I know, this "type" property is inserted automatically only for sealed classes (or its subclasses) with the annotation @Serializable, and the MaskModel is a data class without any interface implementation. My question here is: Does the mask object should have a "type"?
-
The IconImage is converted as a simple property, as it is a value class. The problem here is the missing of the type property, leading to a future deserialization problem (How to know if it is a IconImage or a WebImage?).
To Reproduce
Create the classes above and run.
Expected behavior
The image object should have a type or something that helps to identify its type, avoiding deserialization problems.
Environment
- Kotlin version: [1.8.22]
- Library version: [1.8.22]
- Kotlin platforms: [JVM, Android Native]
- Gradle version: [7.2.2]
- Other relevant context [MacOS Venture, KMM project]
In a similar design to how value classes are boxed into reference classes when they are cast to an interface, I think value classes should become a JSON object and have a "type" field added when they are serialized as an instance of a sealed interface. Unfortunately this will be a pretty major breaking change, so this behavior should probably be exposed by a flag.