Model Polymorphic JSON with Kotlin Sealed Classes

Learn how to use Kotlin sealed classes to represent polymorphic JSON structures where the shape varies by type. Covers discriminator fields and exhaustive pattern matching.

Advanced Patterns

Detailed Explanation

Polymorphic JSON with Sealed Classes

When a JSON field can contain objects of different shapes depending on a discriminator (like "type"), Kotlin sealed classes provide type-safe representation with exhaustive pattern matching.

Example JSON

{
  "type": "text",
  "content": "Hello, world!"
}
{
  "type": "image",
  "url": "https://example.com/photo.jpg",
  "width": 800,
  "height": 600
}

Generated Kotlin

@Serializable
sealed class Message {
    abstract val type: String

    @Serializable
    @SerialName("text")
    data class Text(
        override val type: String = "text",
        val content: String
    ) : Message()

    @Serializable
    @SerialName("image")
    data class Image(
        override val type: String = "image",
        val url: String,
        val width: Int,
        val height: Int
    ) : Message()
}

Exhaustive When Expression

fun render(message: Message) = when (message) {
    is Message.Text -> renderText(message.content)
    is Message.Image -> renderImage(message.url, message.width, message.height)
    // Compiler error if a subtype is not handled
}

kotlinx.serialization Polymorphism

val module = SerializersModule {
    polymorphic(Message::class) {
        subclass(Message.Text::class)
        subclass(Message.Image::class)
    }
}

val json = Json {
    serializersModule = module
    classDiscriminator = "type"
}

When to Use Sealed Classes vs Enum Classes

Feature Enum Sealed Class
Fixed set of values Yes Yes
Each variant holds different data No Yes
Pattern matching Yes Yes
Serializable out of the box Yes Needs setup

Use enum when variants are just labels. Use sealed class when each variant has a different structure.

Sealed Interface (Kotlin 1.5+)

sealed interface Event {
    data class Click(val x: Int, val y: Int) : Event
    data class KeyPress(val key: String) : Event
    data object Logout : Event
}

Sealed interfaces allow a class to implement multiple sealed hierarchies, offering more flexibility than sealed classes.

Use Case

Chat applications, notification systems, and event-driven architectures receive JSON payloads with varying shapes. Sealed classes ensure every variant is handled at compile time, eliminating missed-case bugs in Android UI rendering and server event processing.

Try It — JSON to Kotlin

Open full tool