JSON Serialization Quiz
DATA › Networking
What mechanism does kotlinx.serialization use to produce serializers for @Serializable classes?
- Runtime reflection over the class when the serializer is first used
- A Kotlin compiler plugin that generates code at compile time
- Android annotation processing that only works on the JVM target
- Manual registration of each adapter in a builder before use
Answer: A Kotlin compiler plugin that generates code at compile time
kotlinx.serialization ships a Kotlin compiler plugin that generates a KSerializer per @Serializable type at compile time, avoiding runtime reflection and working across KMP targets.
A Kotlin data class is decoded with kotlinx.serialization and the JSON contains an extra field the class does not declare. With default settings, what happens?
- The extra field is silently ignored
- It is stored in a catch-all map property
- A SerializationException is thrown
- The whole object decodes to null
Answer: A SerializationException is thrown
By default kotlinx.serialization is strict and throws on unknown keys; you must build Json { ignoreUnknownKeys = true } to skip them.
Which is the key advantage of Moshi's @JsonClass(generateAdapter = true) codegen over KotlinJsonAdapterFactory?
- It supports private and protected properties that codegen cannot
- It skips kotlin-reflect and works without runtime reflection
- It is the only option that allows @Json(name=) renaming in practice
- It automatically ignores all unknown JSON keys by default at runtime
Answer: It skips kotlin-reflect and works without runtime reflection
Codegen generates adapters at compile time via KSP, so it does not pull in the ~2.5 MiB kotlin-reflect library and incurs no runtime reflection cost; the reflective factory is what needs kotlin-reflect.
In kotlinx.serialization, how do you rename a property so it maps to a differently-named JSON key?
- Annotate the property with @SerialName("json_key")
- Annotate the property with @Json(name = "json_key")
- Annotate the property with @SerializedName("json_key")
- Override the field name in the Json builder
Answer: Annotate the property with @SerialName("json_key")
@SerialName is the kotlinx.serialization annotation for renaming. @Json is Moshi and @SerializedName is Gson, so those are the wrong libraries.
A non-nullable Kotlin property has no default value, and the incoming JSON omits its key. In kotlinx.serialization, what is the result?
- The property is set to null
- The property gets the type's zero value (0, false, empty string)
- Decoding throws a MissingFieldException
- The default Json instance inserts a placeholder and logs a warning
Answer: Decoding throws a MissingFieldException
With no default and a non-nullable type, a missing key cannot be satisfied, so kotlinx.serialization throws MissingFieldException; only properties with defaults or nullable types tolerate absence.
Why is Gson considered a legacy choice for new Kotlin Android code compared to kotlinx.serialization and Moshi?
- It cannot parse a JSON array without a custom TypeToken
- It only works on Kotlin Multiplatform, never plain Android
- It requires the kotlinx serialization Gradle plugin to compile
- Reflection-based, ignores Kotlin nullability, and unmaintained
Answer: Reflection-based, ignores Kotlin nullability, and unmaintained
Gson uses runtime reflection, can instantiate non-null Kotlin properties as null and skips default values, and is no longer actively developed, which makes it risky for Kotlin code.
What is the standard way to serialize a sealed class hierarchy polymorphically with kotlinx.serialization?
- Register a RuntimeTypeAdapterFactory in the Json builder configuration
- Mark parent and subclasses @Serializable; a discriminator selects it
- Implement Parcelable on every subclass so the subtype is preserved
- Add @JsonClass(generateAdapter = true) onto each sealed subclass
Answer: Mark parent and subclasses @Serializable; a discriminator selects it
Marking the sealed parent and its subclasses @Serializable lets the plugin emit a class discriminator (default key 'type') that encodes and selects the correct subtype; RuntimeTypeAdapterFactory and @JsonClass belong to other libraries.
In kotlinx.serialization, how do you exclude a property of a @Serializable class from being serialized and deserialized?
- Mark it with @Json(ignore = true)
- Make it private, since private properties are skipped automatically
- Mark it with @Expose(serialize = false, deserialize = false)
- Mark it with @Transient and give it a default value
Answer: Mark it with @Transient and give it a default value
@Transient excludes a property from serialization, and the property must have a default value because it will never be read from JSON. @Json is Moshi and @Expose is Gson.
By default kotlinx.serialization does not write a property whose value equals its declared default. How do you force those defaults to be emitted?
- Set ignoreUnknownKeys = true on the Json instance instead
- Set encodeDefaults = true on Json, or use @EncodeDefault
- Defaults are always written out, so there is nothing to enable
- Pass an EncodeDefaults flag to encodeToString at the call site
Answer: Set encodeDefaults = true on Json, or use @EncodeDefault
encodeDefaults defaults to false, so values equal to the default are skipped; setting encodeDefaults = true on the Json config (or @EncodeDefault on a property) forces them to be written.
You need non-default encoding for a type you do not own, such as java.time.Instant, in kotlinx.serialization. What is the idiomatic approach?
- Implement KSerializer<Instant>, attach via @Serializable(with=)
- Register a TypeAdapter via GsonBuilder for the Json instance
- Wrap it in a class annotated with @JsonClass(generateAdapter = true)
- Override toString() on a wrapper the plugin calls automatically
Answer: Implement KSerializer<Instant>, attach via @Serializable(with=)
For types you do not own or that need custom encoding, you implement KSerializer<T> and attach it with @Serializable(with=) or contextual serialization; GsonBuilder and @JsonClass belong to other libraries.
An evolving API sometimes sends JSON null for a field that maps to a non-nullable Kotlin property which has a default value. Which Json setting lets decoding fall back to the default instead of throwing?
- ignoreUnknownKeys = true, which skips unknown JSON keys
- isLenient = true, which relaxes strict JSON quoting
- coerceInputValues = true, falling back to the default
- explicitNulls = true, which always writes null fields
Answer: coerceInputValues = true, falling back to the default
coerceInputValues = true coerces an invalid or null input for a property that has a default into that default value; isLenient only relaxes quoting and ignoreUnknownKeys only skips unknown keys.
Which annotation processor does modern Moshi use to run @JsonClass(generateAdapter = true) codegen on Android?
- kapt, which is required for all Moshi codegen
- KSP (Kotlin Symbol Processing)
- Java's javac annotation processing rounds
- An R8 rule generated at link time
Answer: KSP (Kotlin Symbol Processing)
Modern Moshi codegen runs through KSP for faster, Kotlin-aware processing; the generated XxxJsonAdapter is then discovered automatically by Moshi at runtime.
How do you wire a kotlinx.serialization Json instance into Retrofit as the body converter?
- Retrofit just uses @Serializable models directly, with no converter
- Add GsonConverterFactory.create(json) to the Retrofit builder
- Add MoshiConverterFactory.create() to the Retrofit builder for bodies
- Add Json.asConverterFactory(...) from the serialization converter
Answer: Add Json.asConverterFactory(...) from the serialization converter
Retrofit integrates with kotlinx.serialization via the converter that exposes Json.asConverterFactory(...); the Gson and Moshi factories are for those libraries, and Retrofit has no built-in @Serializable support.
A server renamed a JSON key from 'login' to 'username' but still emits the old key for some clients. In kotlinx.serialization, how can one Kotlin property accept either key when decoding?
- Combine both names in a single @SerialName("username,login")
- Declare a second @Transient property for the legacy key
- Annotate the property with @JsonNames("username", "login")
- Set isLenient = true so any similar key matches
Answer: Annotate the property with @JsonNames("username", "login")
@JsonNames declares additional accepted input names for a property during decoding, so both 'username' and 'login' deserialize into the same property; @SerialName accepts only a single name.