JSON to Kotlin: data classes from a sample
Kotlin's data class is the natural shape for JSON models: immutable properties, generated equals, hashCode, toString and copy. This JSON to Kotlin generator writes data classes from a sample, one per object shape, using val properties, Kotlin types, and nullable types where the data shows a value can be missing or null.
Example
{
"id": 42,
"title": "Write docs",
"done": false,
"priority": 2.5,
"tags": ["docs"],
"owner": { "name": "Ada", "email": null },
"due": "2026-10-01T09:00:00Z",
"subtasks": [
{ "title": "Outline", "estimate": 2 },
{ "title": "Draft" }
]
}
With Types only checked:
package quicktype
import java.time.OffsetDateTime
data class Task (
val id: Long,
val title: String,
val done: Boolean,
val priority: Double,
val tags: List<String>,
val owner: Owner,
val due: OffsetDateTime,
val subtasks: List<Subtask>
)
data class Owner (
val name: String,
val email: Any? = null
)
data class Subtask (
val title: String,
val estimate: Long? = null
)
Optional properties are nullable and default to null, so constructing a Subtask without an estimate compiles. The date string became java.time.OffsetDateTime, which is available on the JVM and on Android API 26+.
Jackson helpers in full mode
With Types only off, the output targets Jackson with the Kotlin module: properties get @get:JsonProperty(required=true) and @field:JsonProperty(required=true) annotations, a preconfigured mapper handles OffsetDateTime and skips nulls when writing, and each top-level class gets toJson() and Task.fromJson(jsonString). Add com.fasterxml.jackson.module:jackson-module-kotlin to your dependencies, as the header comment explains.
Using kotlinx.serialization, Moshi or Gson instead
The types-only classes are a good base for other libraries:
- kotlinx.serialization: annotate each class with
@Serializable, add@SerialName("user_name")where keys differ, and use aStringor a custom serializer for dates, sinceOffsetDateTimehas no built-in serializer. - Moshi: add
@JsonClass(generateAdapter = true)and@Json(name = "..."), the usual choice in Android apps using Retrofit. - Retrofit: declare
suspend fun getTask(@Path("id") id: Long): Taskin your service interface and register a converter factory for the library you chose; the data class is used as the return type directly. - Gson: works with data classes, but it ignores Kotlin's null-safety and default values, so a missing non-null field can end up null at runtime.
Whatever library you use, property names are converted to camelCase (is_active becomes isActive), so add name annotations when the JSON uses snake_case.
Null safety and missing keys
Kotlin's type system makes a promise: a String is never null. JSON libraries must keep that promise when data is incomplete. With Jackson's Kotlin module, a missing value for a non-null property without a default causes a MismatchedInputException (reported for Kotlin as a missing parameter). kotlinx.serialization throws MissingFieldException. Both are good: the error appears at the boundary instead of as a NullPointerException later. If an API field is sometimes absent, make the property nullable with a default, like the generated estimate: Long? = null.
Type mapping
| JSON | Kotlin |
|---|---|
| integer / decimal | Long / Double |
| boolean | Boolean |
| ISO date-time | OffsetDateTime |
| array | List<T> |
| optional / nullable | T? = null |
| null only | Any? |
Replace the quicktype package with your own. For XML sources, use XML to Kotlin; for Java models, JSON to Java. Check the input with the JSON Validator. Generation runs in your browser.