With kotlinx.serialization, what happens when you mark a sealed class @Serializable and serialize one of its subclasses to JSON? What does the output look like?
answer
- sealed => compiler auto-registers subclasses
- default discriminator key = "type"
- discriminator value = serial name (@SerialName)
- annotate base AND every subclass @Serializable
- decode via base serializer
basics
~10 skotlinx.serialization adds a special field (by default named "type") to the JSON that records which subclass it is. When reading back, that field tells the library which subclass to build.
solid answer
~40 sIf you annotate a sealed class with @Serializable and annotate each subclass with @Serializable, kotlinx.serialization treats the hierarchy as polymorphic. Because the compiler plugin knows all subclasses of a sealed class at compile time, it auto-registers them — no manual SerializersModule needed. Serializing a subclass produces a JSON object containing a class-discriminator field (default key "type") whose value is the subclass's fully-qualified serial name, plus the subclass's own properties. On decode, the library reads the discriminator first, selects the matching subclass serializer, and deserializes the rest. The discriminator key is configurable via Json { classDiscriminator = "..." } and the value via @SerialName on each subclass. This auto-registration is the key advantage over open/abstract polymorphism, which requires explicit registration.
code
kotlin · 11 lines@Serializable
sealed class Shape {
@Serializable @SerialName("circle")
data class Circle(val r: Double) : Shape()
@Serializable @SerialName("rect")
data class Rect(val w: Double, val h: Double) : Shape()
}
val s: Shape = Shape.Circle(2.0)
println(Json.encodeToString(s)) // {"type":"circle","r":2.0}
val back = Json.decodeFromString<Shape>("""{"type":"rect","w":1.0,"h":2.0}""")go deeper
Knows sealed + @Serializable on base and subclasses produces a 'type' field automatically.
Can change the discriminator key/value and explain decode lookup by discriminator.
Explains why sealed enables compile-time auto-registration and contrasts with open hierarchies.
Frames discriminator naming as a wire-contract decision (stable @SerialName values, not class names) for forward/backward compatibility.
## What polymorphic serialization means "Polymorphic" here means: you have a base type (a sealed class or interface) and several concrete subtypes, and you want to serialize/deserialize values through the **base** type while preserving **which concrete subtype** each value actually is. JSON has no built-in notion of "class", so kotlinx.serialization stores the type identity in a **class discriminator** field. ## Why sealed classes are special A `sealed` class/interface restricts its subclasses to the same compilation unit (module/package), so the **compiler knows the complete set of subtypes**. The kotlinx.serialization compiler plugin uses this to **auto-register** every subclass — you do not write any `SerializersModule`. ## What you must annotate - The sealed base type: `@Serializable` - **Every** concrete subclass: `@Serializable` ```kotlin import kotlinx.serialization.* import kotlinx.serialization.json.Json @Serializable sealed class Event { @Serializable data class Click(val x: Int, val y: Int) : Event() @Serializable data class Key(val code: Int) : Event() } val json = Json.encodeToString(Event.serializer(), Event.Click(3, 4)) // {"type":"Event.Click","x":3,"y":4} ``` ## The discriminator - **Key**: default `"type"`. Change globally with `Json { classDiscriminator = "_t" }`. - **Value**: the **serial name** of the subclass. By default this is the fully-qualified class name; override per-subclass with `@SerialName("click")`. ```kotlin @Serializable @SerialName("click") data class Click(val x: Int, val y: Int) : Event() // -> {"type":"click","x":3,"y":4} ``` ## Decoding Decode through the **base** serializer (`Event.serializer()`). The library reads the discriminator first, looks up the registered subclass, and builds it. If the discriminator value matches no registered subtype, it throws `SerializationException`. ## Key APIs/keywords `@Serializable`, `@SerialName`, `sealed`, `Json.encodeToString`, `Json { classDiscriminator = ... }`, `SerializationException`.
- How do you change the discriminator value without changing the Kotlin class name?Put @SerialName("...") on the subclass; the serial name becomes the discriminator value.
- What if you forget @Serializable on one subclass?Compilation/serialization fails for that subclass; the plugin can't generate a serializer, so it can't be auto-registered.
The discriminator is like a shipping label on a box: the contents are the fields, the label tells the receiver which kind of box to unpack it as.
saying these in an interview costs you the question
- Claiming you must always write a SerializersModule for sealed classes
- Saying the discriminator is stored as a JSON array element rather than an object field
- Thinking only the base class needs @Serializable
- Confusing the discriminator KEY (default 'type') with its VALUE (the serial name)