We indeed blur the line between static and dynamic somewhat. Moreover, we already support a certain level of “dynamicity” for JSON in a non-standard, but Jackson-complian way. It is still a work in progress (only on JVM and with many limitations), but consider this case anyway:
@Serializable open class Base { var a: Int = 42 }
@Serializable class Derived : Base()
@Serializable class Container(val b: Base)
Now, evaluating JSON.stringify(Container(Derived()))
produces {"b":["Derived",{"a":42}]}
. As you can see, “dynamic” runtime type of “Derived” was represented with an array of the type name and the actual value, so deserializing it produces the original types which can be verified by evaluating JSON.parse<Container>("{b:[Derived,{a:42}]}").b::class
which produces class Derived
.
On JVM this kind of deserialization is currently implemented via reflection, which, indeed, opens the same can of worms and any other form a dynamic serialization, however, you don’t have to use it and, unlike a standard JVM deserialization (which is always reflective), here reflection only kicks in if you have explicitly declared your serializable class to be open
. We also plan to support “closed world” dynamic serialization where you explicitly list all the serializable classes. On Kotlin/Native and Kotlin/JS that is going to be the only way, as both platforms are currently designed with “closed world” assumption in mind and perform dead code elimination (DCE) based on that assumption.