Tools
JSON to Kotlin
Paste JSON, get null-safe Kotlin data classes —@Serializable with @SerialName for awkward keys, defaults where keys may be absent. Runs entirely in your browser.
Generated code
More on this site: all tools ·JSON formatter ·regex checker ·the arcade
Beta — the generated code is unit- and compile-tested, but inference from one sample can't be perfect; on rare occasions the output may need a tweak. Spot something wrong?Tell me.
What is this tool?
A free JSON-to-Kotlin converter. Paste a JSON document — an API response, a config export — and it generates idiomaticKotlin data classes for the whole tree, annotated for kotlinx.serialization (JetBrains' official, multiplatform serializer): @Serializable on every class, @SerialName wherever a JSON key isn't a clean identifier, and default values exactly where your sample shows a key can be absent. A plain data class style (no annotations) is one click away for Gson/Moshi codebases.
Your JSON never leaves the browser. Parsing, inference and generation are client-side; the page works offline once loaded.
How to use it
- Paste JSON on the left — Kotlin appears on the right as you type.
- Name the root class; nested classes are named from their keys, singularized inside lists (
users: […]→User). - Decode with
Json { ignoreUnknownKeys = true }.decodeFromString<Response>(raw)— the usage line ships in the generated header. - Check the warnings panel: every place the sample was too thin (empty array, always-null field) is named by JSON path, not guessed.
- Copy or Download the
.ktfile.
Nullable vs absent — kotlinx gets this exactly right
kotlinx.serialization distinguishes the two things most converters flatten, and the generated code uses both deliberately:
val note: String? // key must be present; value may be null
val lastLogin: String? = null // key may be absent entirelyA nullable type without a default still requires the key in the payload — decoding fails loudly if it's gone, which is what you want when your sample says it's always sent. A default makes the property optional to the decoder. The tool assigns each from evidence: an explicit null in your sample produces the first form, a key missing from one list element produces the second.
Why Long, and other type choices
Longfor integers. JSON IDs routinely exceedInt's 2³¹ — the overflow bug is silent and production-only. If you know a field is small, tighten it by hand.Doublefrom the raw token. A field that ever showed a decimal point (4.5) becomesDoubleeven if another sample said5— the tool reads number tokens itself rather than trusting JavaScript's parsing, which erases the difference.- Dates stay
String, on purpose.kotlinx.serialization has no built-in date serializer — emittingInstantorLocalDateTimewould generate code that doesn't compile without a contextual serializer you'd have to write. Detected date fields are typedStringwith an// ISO 8601note; parse at the use site withkotlinx-datetimeorjava.time.Instant.parse(…). JsonElementfor the unknowable.Any?has no serializer; when the sample can't pin a type down (always null, empty array, mixed types), the field iskotlinx.serialization.json.JsonElement— decodable, inspectable, honest. The warning names the path so you can fix the sample instead.
kotlinx.serialization or plain data classes?
| Style | Best for | Trade-off |
|---|---|---|
| kotlinx.serialization (default) | New code, Kotlin Multiplatform, Ktor — the official compiler-plugin route, no reflection. | Needs the plugin + runtime dependency in your build. |
| Plain data classes | Gson/Moshi codebases — types only, renamed fields carry a // JSON key: … note to hand to @SerializedName/@Json. | No serialization included; reflection-based libraries won't respect nullability at runtime (Gson happily writes null into non-null vals — a classic Kotlin/Gson trap). |
Worked example
Load the Sample and look at what comes back:
"created-at"→@SerialName("created-at") val createdAt: String— clean identifier, exact round-trip."rating"merges4.5and5→Double."last_login"missing from one user →String? = null."note": null→JsonElement?plus a warning that the sample never showed its type."users"→List<User>, named by singularizing the key.
FAQ
Is it free? Yes — free, no sign-up, no limits.
Is my JSON uploaded anywhere? No — fully client-side, works offline after load; only your last input is kept, in your own browser's local storage.
Why ignoreUnknownKeys in the usage line?By default kotlinx throws on any key your classes don't declare — robust for contracts you control, brittle against evolving APIs. The suggested configuration tolerates additions; delete it if you want strictness.
Moshi or Gson annotations? Not emitted yet — the plain style plus its // JSON key notes maps onto@Json(name = …)/@SerializedName(…) in seconds. A dedicated Moshi style may follow demand.
Can it read a JSON Schema? Not yet — v1 infers from samples; schema input is planned.
Built from scratch for this site in vanilla TypeScript — a pure, unit-tested inference engine with golden-tested emitters and zero runtime dependencies.