JSON to Kotlin Data Class
Generate Kotlin data classes for kotlinx.serialization from JSON. Imports included, Int / Long / Double by value, JsonElement for unknown types, nested classes. Runs in your browser.
- Runs in your browser
- Your data never leaves your browser
- Free · No Sign-Up
Scan with WeChat to share this tool
Examples, details and FAQ Worked examples, how it compares with other tools, and answers to common questions.
Type Mapping
- string →
String; boolean →Boolean - whole number from −2,147,483,648 to 2,147,483,647 →
Int; larger →Long; beyond theLongrange →Double - number written with a decimal point or exponent (
9.5,19.0,1e3) →Double - array →
List<T>;Int,LongandDoubleelements widen to the largest; other mixes, and empty arrays, giveList<JsonElement> null→ nullable type; a field that is onlynull→JsonElement?- nested object → separate
data class; an empty object{}→class Name(a data class needs at least one property)
Example: snake_case API Response
Input with snake_case keys, a null field, a counter above Int.MAX_VALUE and a rating written as 4.0:
{"user_id": 1024, "display_name": "Alice", "is_active": true, "last_login": null, "follower_count": 3000000000, "rating": 4.0, "tags": ["admin", "editor"], "address": {"city": "Tokyo", "zip_code": "100-0001"}}
Output:
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
@Serializable
data class RootObject(
@SerialName("user_id")
val userId: Int = 0,
@SerialName("display_name")
val displayName: String = "",
@SerialName("is_active")
val isActive: Boolean = false,
@SerialName("last_login")
val lastLogin: JsonElement? = null,
@SerialName("follower_count")
val followerCount: Long = 0L,
val rating: Double = 0.0,
val tags: List<String> = emptyList(),
val address: Address = Address()
)
@Serializable
data class Address(
val city: String = "",
@SerialName("zip_code")
val zipCode: String = ""
)
Each key whose camelCase name differs from the JSON key keeps the original in @SerialName. Kotlin keywords used as keys (class, in, fun) are written in backticks. last_login is only ever null in the sample, so it is JsonElement?; change it to String? once you know it holds a timestamp.
Example: Array of Objects
When the root is an array, the tool merges all objects into one class. price is 2 in one item and 1.5 in the other, so it becomes Double. A key that is missing from some items becomes nullable with null as the default:
[{"id": 1, "name": "Pen", "price": 2}, {"id": 2, "name": "Ink", "price": 1.5, "discount": 0.1}]
import kotlinx.serialization.Serializable
@Serializable
data class RootObject(
val id: Int = 0,
val name: String = "",
val price: Double = 0.0,
val discount: Double? = null
)
The status line reminds you to decode this input as List<RootObject>.
Example: Same Key, Different Objects
Two nested objects called meta with different fields get two classes. The second takes its parent’s name as a prefix:
{"order": {"id": 7, "meta": {"source": "web"}}, "payment": {"meta": {"method": "card", "last4": "4242"}}}
import kotlinx.serialization.Serializable
@Serializable
data class RootObject(
val order: Order = Order(),
val payment: Payment = Payment()
)
@Serializable
data class Order(
val id: Int = 0,
val meta: Meta = Meta()
)
@Serializable
data class Meta(
val source: String = ""
)
@Serializable
data class Payment(
val meta: PaymentMeta = PaymentMeta()
)
@Serializable
data class PaymentMeta(
val method: String = "",
val last4: String = ""
)
Decoding
Add the serialization plugin and the JSON library to your build (see the Kotlin serialization guide), then decode:
import kotlinx.serialization.json.Json
val user = Json.decodeFromString<RootObject>(body)
val items = Json.decodeFromString<List<RootObject>>(arrayBody)
// tolerate keys the sample did not have
val lenient = Json { ignoreUnknownKeys = true }
The default Json rejects keys that are not in the class. Every example on this page was compiled with Kotlin 2.4.20 and kotlinx.serialization 1.11.0 and decoded with that default configuration.
Limitations
- Types come from one sample. An ID that is small in the sample is typed
Inteven if production IDs exceed 2,147,483,647; change it toLongyourself. - The browser parses JSON numbers as 64-bit floating point, so integers above 9,007,199,254,740,991 lose digits before the type is chosen. The type is still
Long, but check the range by hand. 19.0is recognised as a decimal only in browsers that giveJSON.parsethe source text (Chrome 114+, Firefox 135+, Safari 18.4+). Older browsers type itInt.JsonElementfields work with theJsonformat only, not with CBOR or ProtoBuf.- Dates stay
String. JSON has no date type; parse them after decoding or add a custom serializer. - Every property has a default value, so a missing key decodes silently. Remove a default if that key must be present.
FAQ
What does this tool generate?
Kotlin data classes annotated with @Serializable for kotlinx.serialization, with the import lines they need. Each nested object becomes its own class. The output compiles with the kotlinx.serialization compiler plugin and decodes the sample with the default Json configuration.
How are nullable fields handled?
If a JSON field's value is null, the Kotlin type is marked nullable (e.g. String?). Fields missing from some array items are also nullable and default to null. A field that is only ever null becomes JsonElement?, because the sample does not show its real type.
What are the default values for each type?
String defaults to "", Int to 0, Long to 0L, Double to 0.0, Boolean to false, List to emptyList(), JsonElement to JsonNull, nullable types to null, and nested classes call their no-argument constructor. Defaults let a missing key decode instead of failing.
Why does the output use JsonElement instead of Any?
kotlinx.serialization has no serializer for Any, so a @Serializable class with an Any field does not compile. JsonElement is the type the library provides for JSON of unknown shape. It is used for null-only fields, empty arrays and values whose type differs between samples.
Is my data sent to a server?
No. The entire conversion runs in your browser. No data leaves your machine.