Guide
Kotlin data classes for kotlinx.serialization
Kotlin’s data classes are a natural fit for JSON models, and kotlinx.serialization, the official JetBrains library, turns them into fast, reflection-free parsers on Android, the JVM and Kotlin Multiplatform. This generator writes the data classes for you from a sample payload, with the annotations the library needs.
Setting up the library
Add the serialization compiler plugin and the JSON runtime to your Gradle build. The plugin generates a serializer for every class marked @Serializable at compile time, which is why the output carries that annotation on every class. Nothing else is required to decode: Json.decodeFromString<User>(text).
Names and @SerialName
Kotlin properties are camelCase. When the JSON key is already camelCase, the property uses it as is. Otherwise the property is renamed and annotated, for example @SerialName("order_id") val orderId: Long, so the Kotlin code reads naturally while the wire format stays unchanged. Keys that clash with Kotlin keywords, such as object or when, are written in backticks.
Nullability and defaults
Kotlin’s type system separates nullable from non-null types, and the generator uses that precisely. A key present with a value in every sample becomes a non-null type. A key missing from some samples, or null in some, becomes nullable with a default: val coupon: String? = null. The default matters, because kotlinx.serialization decides whether a key is optional by whether the property has a default value.
Decoding options
APIs grow. By default kotlinx.serialization throws when the JSON contains a key the class does not declare, so create your decoder as Json { ignoreUnknownKeys = true } for third-party APIs. coerceInputValues = true additionally replaces nulls in non-null properties with their defaults, and explicitNulls = false stops encoding from writing nulls for absent values.
Loose values
When a field was always null or had different types in different samples, it becomes JsonElement, which can hold any JSON value; the import is added only when needed. Read it with jsonPrimitive, jsonObject or jsonArray once you know what it holds.
A worked example
From the sample user, the generator writes four data classes: User, Order, Item and Address. The order’s order_id becomes @SerialName("order_id") val orderId: Long, the coupon that only one order has becomes val coupon: String? = null, and the always-null unit in the address becomes val unit: JsonElement? = null.
Using Moshi or Gson instead
The class shapes work with other libraries too. For Moshi, replace @Serializable with @JsonClass(generateAdapter = true) and @SerialName with @Json(name = …). For Gson, use @SerializedName, and remember that Gson ignores Kotlin’s nullability and default values, which is one reason many Android teams have moved to kotlinx.serialization or Moshi.
In Retrofit and Ktor
Retrofit has an official kotlinx.serialization converter (asConverterFactory), and Ktor’s ContentNegotiation plugin uses the same Json instance, so these classes plug into both with the decoder configuration above.
Testing the models
Keep a real response in your test resources and decode it in a unit test with the same Json configuration as the app. When the API changes a type or removes a field, the test fails with the key name, rather than a crash appearing on a user’s device.
Related tools
Generate Java records with JSON to Java, Swift models for the iOS app with JSON to Swift, Flutter classes with JSON to Dart, or check the payload in the JSON Formatter.
Checked against the current version of the JSON to Kotlin Data Class on .

