Skip to content

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.

Style:

Generated code

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 .kt file.

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 entirely

A 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

  • Long for integers. JSON IDs routinely exceed Int's 2³¹ — the overflow bug is silent and production-only. If you know a field is small, tighten it by hand.
  • Double from the raw token. A field that ever showed a decimal point (4.5) becomesDouble even if another sample said 5 — 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 — emittingInstant or LocalDateTime would generate code that doesn't compile without a contextual serializer you'd have to write. Detected date fields are typedString with an // ISO 8601 note; parse at the use site with kotlinx-datetime orjava.time.Instant.parse(…).
  • JsonElement for 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?

StyleBest forTrade-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 classesGson/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" merges 4.5 and 5Double.
  • "last_login" missing from one user → String? = null.
  • "note": nullJsonElement? 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.