You add a new property to a kotlinx.serialization @Serializable class, but old JSON payloads don't contain it. How do you make deserialization of those old payloads still succeed?
answer
- Missing key + no default = MissingFieldException
- Add property WITH a default value
- Nullable != default; need '= null'
- Defaults give backward compatibility
- Defaults are evaluated at decode time
basics
~10 sGive the new property a default value. When the field is missing from the JSON, kotlinx.serialization uses the default, so old messages still load without errors.
solid answer
~40 sAdd the new property with a default value, e.g. `val role: String = "USER"`. kotlinx.serialization treats a missing JSON key as 'use the default', so payloads created before the field existed still deserialize. Without a default, a missing required key throws `MissingFieldException` (a `SerializationException`). Defaults are the primary backward-compatibility tool: new readers can consume old data. Note the default expression must be valid Kotlin and is evaluated at decode time. If you also want to tolerate the inverse — old readers seeing keys they don't know — that's a separate concern solved by `ignoreUnknownKeys`. For optional-and-absent semantics you can combine a default with a nullable type (`val role: String? = null`) to distinguish 'absent' handling from a real value.
code
kotlin · 9 lines@Serializable
data class Config(
val host: String,
val port: Int = 8080, // added later; old JSON omits it
val tls: Boolean = false
)
val old = """{"host":"db"}"""
val c = Json.decodeFromString<Config>(old) // port=8080, tls=falsego deeper
Knows to add a default value so old payloads still deserialize.
Distinguishes nullable vs default and names MissingFieldException.
Connects the choice to backward vs forward compatibility and encoding omission of defaults.
Frames defaults as part of a versioning policy and weighs payload-size vs explicitness tradeoffs across services.
## The problem: schema evolution Schema evolution means changing a serialized data shape over time while old and new versions still interoperate. **Backward compatibility** = a new reader can read old data. **Forward compatibility** = an old reader can read new data. ## Adding a field (backward compatibility) When you add a property to a `@Serializable` class, JSON written by the previous version won't contain that key. kotlinx.serialization's rule is simple: - **No default, key missing** → throws `MissingFieldException` (subtype of `SerializationException`). - **Has a default, key missing** → the default is used. So the fix is to always give newly added properties a **default value**. ```kotlin import kotlinx.serialization.* import kotlinx.serialization.json.Json @Serializable data class User( val id: Long, val name: String, val role: String = "USER" // added in v2; old JSON lacks it ) val oldJson = """{"id":1,"name":"Ada"}""" val user = Json.decodeFromString<User>(oldJson) // user.role == "USER" (default applied, no exception) ``` ## Why not just make it nullable? A nullable type `String?` is itself a value, not a default. `val role: String?` with **no** `= null` is still a *required* key — a missing key throws. You must write `val role: String? = null` to make absence acceptable. Nullable answers 'can the value be null?'; default answers 'what if the key is absent?'. They are orthogonal. ## Encoding side By default, kotlinx.serialization **omits** a property whose value equals its default when encoding (see `encodeDefaults`/`@EncodeDefault`). That keeps payloads small but means consumers must rely on defaults too. ## Recall Missing-required-key is the most common runtime serialization failure in evolving systems; defaults are the cheapest cure.
- What exception type is thrown when a required key is missing?MissingFieldException, which is a subclass of SerializationException.
- Does making the property nullable alone fix the missing-key error?No. You must also supply a default (`= null`); a nullable type without a default is still a required key.
A default value is like a form field pre-filled with a sensible answer: if the sender leaves it blank, the form is still valid.
saying these in an interview costs you the question
- Claiming a nullable type alone makes the key optional
- Saying missing keys silently become null without a default
- Believing you must write a custom serializer for a simple added field
- Thinking defaults are evaluated once at class load, not at decode