kotlinx.serialization icon indicating copy to clipboard operation
kotlinx.serialization copied to clipboard

Problem with value classes serialization/deserialization

Open RodrigoFerreira001 opened this issue 2 years ago • 1 comments

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:

  1. 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"?

  2. 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]

RodrigoFerreira001 avatar Jul 20 '23 21:07 RodrigoFerreira001

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.

NatanLifshitz avatar Nov 20 '23 07:11 NatanLifshitz