DevToolBoxฟรี
บล็อก

JSON เป็น Kotlin Data Class: คู่มือ kotlinx.serialization, Moshi และ Gson

16 นาทีในการอ่านโดย DevToolBox

TL;DR

Converting JSON to Kotlin data classes is essential for type-safe Android and server-side development. Use kotlinx.serialization for new projects, Moshi for Retrofit-heavy Android apps, and avoid Gson in new Kotlin code. Kotlin data classes give you null safety, default values, copy(), and destructuring out of the box. Need instant conversion? Try our free JSON to Kotlin converter.

Key Takeaways

  • Kotlin data classes map JSON objects to type-safe, immutable models with built-in equals(), hashCode(), and copy().
  • kotlinx.serialization is the JetBrains-maintained library that uses compile-time codegen, supports Kotlin Multiplatform, and respects null safety.
  • Use @SerialName, @Json(name = ...), or @SerializedName to bridge snake_case JSON keys and camelCase Kotlin properties.
  • Declare optional JSON fields as nullable with defaults (val field: String? = null) to prevent runtime crashes.
  • Nested JSON objects produce separate data classes; arrays become List<T> instead of Array<T>.
  • Sealed classes handle polymorphic JSON with compile-time exhaustiveness in when expressions.
  • Retrofit, Ktor, and Spring Boot all integrate seamlessly with Kotlin serialization libraries for API consumption.

Why Kotlin Data Classes for JSON

When your Android app or Kotlin backend consumes a REST API, the JSON response must be converted into strongly typed objects before your business logic can use it. Kotlin data classes are the ideal target for this conversion because they combine immutability, null safety, and auto-generated utility methods in a concise syntax.

Unlike Java POJOs that require manually writing getters, setters, equals(), hashCode(), and toString(), a Kotlin data class generates all of these from its primary constructor parameters. This eliminates hundreds of lines of boilerplate in a typical API model layer. Combined with Kotlin null safety (String vs String?), you get compile-time protection against NullPointerException that Java cannot provide.

Whether you are building with Retrofit on Android, Ktor for server-side Kotlin, or a Kotlin Multiplatform (KMP) shared module, a reliable JSON to Kotlin data class converter turns raw JSON into production-ready models in seconds. If you need to do this right now, use our online JSON to Kotlin converter.

Basic JSON to Kotlin Conversion

The foundation of JSON to Kotlin conversion is understanding how JSON types map to Kotlin types. Here is the complete mapping table:

JSON TypeExampleKotlin TypeNotes
string"hello"StringNon-nullable by default; use String? when nullable
number (integer)42Int / LongUse Long for values exceeding 2^31-1
number (decimal)3.14Double / BigDecimalUse BigDecimal for financial data
booleantrueBooleanNon-nullable by default
nullnullT?Kotlin distinguishes nullable from non-nullable
array[1, 2]List<T>Prefer immutable List over Array
object{}Nested data classStrongly typed classes preferred over Map<String, Any>

Here is a simple JSON payload and the resulting Kotlin data class:

{
  "id": 1,
  "name": "Alice",
  "email": "alice@example.com",
  "is_active": true,
  "score": 98.5
}
data class User(
    val id: Int,
    val name: String,
    val email: String,
    val isActive: Boolean,
    val score: Double
)

Notice how the JSON key is_active becomes isActive in camelCase. Serialization libraries use annotations to handle this mapping, which we cover in detail below. You can also generate this automatically with our JSON to Kotlin tool.

Nested Objects and Lists

Real-world APIs rarely return flat JSON. Most responses contain nested objects, arrays of objects, and deeply layered structures. Each nested JSON object should produce a separate Kotlin data class for maximum type safety and reusability.

{
  "order_id": 5001,
  "customer": {
    "name": "Bob",
    "email": "bob@example.com",
    "address": {
      "street": "123 Main St",
      "city": "Springfield",
      "zip_code": "62704"
    }
  },
  "items": [
    { "product": "Keyboard", "quantity": 1, "price": 79.99 },
    { "product": "Mouse", "quantity": 2, "price": 29.99 }
  ],
  "total": 139.97
}
import kotlinx.serialization.*

@Serializable
data class Order(
    @SerialName("order_id") val orderId: Long,
    val customer: Customer,
    val items: List<OrderItem>,
    val total: Double
)

@Serializable
data class Customer(
    val name: String,
    val email: String,
    val address: Address
)

@Serializable
data class Address(
    val street: String,
    val city: String,
    @SerialName("zip_code") val zipCode: String
)

@Serializable
data class OrderItem(
    val product: String,
    val quantity: Int,
    val price: Double
)

Each level of nesting produces a separate data class. Prefer top-level declarations over inner classes for better reusability across your codebase. For arrays of objects, always use List<T> instead of Array<T> because List participates correctly in equals() and hashCode() comparisons generated by data classes.

If you also work with Java models, check out our JSON to Java converter for generating POJOs from the same JSON.

Nullable Fields and Default Values

One of Kotlin greatest strengths over Java is its type system that distinguishes between nullable (String?) and non-nullable (String) types at compile time. When converting JSON to Kotlin, you must decide which properties can be null and which ones are always present.

@Serializable
data class UserProfile(
    val id: Long,
    val username: String,                       // required, never null
    val displayName: String? = null,            // optional, may be absent
    val bio: String = "",                       // optional with non-null default
    val followerCount: Int = 0,                 // numeric default
    val isVerified: Boolean = false,            // boolean default
    val avatarUrl: String? = null,              // nullable, may be absent
    val tags: List<String> = emptyList()        // empty list default
)

With kotlinx.serialization, properties that have default values are automatically treated as optional during deserialization. If the JSON payload omits displayName, the data class constructor uses null as the default. If the JSON omits bio, it defaults to an empty string. This behavior prevents crashes when APIs evolve and start omitting fields.

Always enable ignoreUnknownKeys = true in your Json builder so new fields added by the backend do not break your client:

val json = Json {
    ignoreUnknownKeys = true    // ignore new fields from the server
    isLenient = true            // accept slightly malformed JSON
    coerceInputValues = true    // coerce nulls to defaults for non-null types
}

kotlinx.serialization Setup and Usage

kotlinx.serialization is the official Kotlin serialization library maintained by JetBrains. It uses compile-time code generation instead of reflection, resulting in faster performance and smaller binary sizes. It is the recommended choice for all new Kotlin projects, including Android, KMP, and server-side.

Gradle setup (Kotlin DSL):

// build.gradle.kts
plugins {
    kotlin("jvm") version "1.9.22"
    kotlin("plugin.serialization") version "1.9.22"
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
}

Complete usage example:

{
  "user_id": 1001,
  "user_name": "Alice",
  "email": "alice@example.com",
  "is_active": true,
  "balance": 1250.75,
  "tags": ["admin", "developer"],
  "address": {
    "street": "123 Main St",
    "city": "Springfield",
    "zip_code": "62704"
  }
}
import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class User(
    @SerialName("user_id") val userId: Long,
    @SerialName("user_name") val userName: String,
    val email: String,
    @SerialName("is_active") val isActive: Boolean,
    val balance: Double,
    val tags: List<String> = emptyList(),
    val address: Address? = null
)

@Serializable
data class Address(
    val street: String,
    val city: String,
    @SerialName("zip_code") val zipCode: String
)

// Configure the Json instance
val json = Json {
    ignoreUnknownKeys = true
    isLenient = true
    coerceInputValues = true
}

// Deserialize a single object
val user: User = json.decodeFromString(jsonString)

// Deserialize a list of objects
val users: List<User> = json.decodeFromString(jsonArrayString)

// Serialize back to JSON
val outputJson: String = json.encodeToString(user)

The @SerialName annotation maps snake_case JSON keys to camelCase Kotlin properties. The @Serializable annotation triggers compile-time adapter generation, so no reflection is needed at runtime.

Gson and Moshi Alternatives

While kotlinx.serialization is the recommended library, many Android projects use Moshi or Gson. Here is how both compare for JSON to Kotlin conversion.

Moshi (Recommended for Android with Retrofit)

import com.squareup.moshi.*
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory

@JsonClass(generateAdapter = true)
data class User(
    @Json(name = "user_id") val userId: Long,
    @Json(name = "user_name") val userName: String,
    val email: String,
    @Json(name = "is_active") val isActive: Boolean,
    val balance: Double,
    val tags: List<String> = emptyList(),
    val address: Address? = null
)

// Build the Moshi instance
val moshi: Moshi = Moshi.Builder()
    .addLast(KotlinJsonAdapterFactory())
    .build()

// Deserialize
val adapter: JsonAdapter<User> = moshi.adapter(User::class.java)
val user: User? = adapter.fromJson(jsonString)

// Deserialize a list
val listType = Types.newParameterizedType(List::class.java, User::class.java)
val listAdapter: JsonAdapter<List<User>> = moshi.adapter(listType)
val users: List<User>? = listAdapter.fromJson(jsonArrayString)

Moshi respects Kotlin null safety and throws clear errors when a non-nullable field receives null. The @JsonClass(generateAdapter = true) annotation enables compile-time adapter generation, avoiding reflection overhead.

Gson (Legacy Projects Only)

import com.google.gson.Gson
import com.google.gson.annotations.SerializedName

// WARNING: Gson does NOT respect Kotlin null safety!
data class User(
    @SerializedName("user_id") val userId: Long,
    @SerializedName("user_name") val userName: String,
    val email: String,
    @SerializedName("is_active") val isActive: Boolean,
    val balance: Double,
    val tags: List<String> = emptyList(),
    val address: Address? = null  // always use ? with Gson
)

val gson = Gson()
val user: User = gson.fromJson(jsonString, User::class.java)

// SAFETY TIP: validate after Gson deserialization
fun User.validate(): User {
    requireNotNull(userName) { "userName must not be null" }
    requireNotNull(email) { "email must not be null" }
    return this
}

Avoid Gson for new Kotlin projects. Gson was designed for Java and uses reflection that bypasses Kotlin constructors. It can silently assign null to non-nullable properties, leading to crashes far from the deserialization site. If you are maintaining a legacy codebase, always declare fields as nullable when using Gson or add explicit validation.

Retrofit Integration for API Calls

Retrofit is the most popular HTTP client for Android development. It integrates with all three JSON libraries through converter factories. Here is a complete Retrofit setup using kotlinx.serialization:

import retrofit2.Retrofit
import retrofit2.http.GET
import retrofit2.http.Path
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType

// Define your API interface
interface UserApi {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") userId: Long): User

    @GET("users")
    suspend fun getUsers(): List<User>
}

// Create the Retrofit instance
val contentType = "application/json".toMediaType()
val json = Json { ignoreUnknownKeys = true }

val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(json.asConverterFactory(contentType))
    .build()

val api = retrofit.create(UserApi::class.java)

// Usage in a ViewModel or coroutine scope
val user = api.getUser(1001)
val allUsers = api.getUsers()

For Moshi, replace the converter factory with MoshiConverterFactory.create(moshi). For Ktor on server-side or KMP projects, use the built-in ContentNegotiation plugin with kotlinx.serialization:

// Ktor client setup
val client = HttpClient(CIO) {
    install(ContentNegotiation) {
        json(Json { ignoreUnknownKeys = true })
    }
}

// Usage
val user: User = client.get("https://api.example.com/users/1001").body()

For Flutter/Dart projects consuming the same API, see our JSON to Dart converter.

Best Practices: Naming, @SerializedName, and @Json

Follow these best practices when you convert JSON to Kotlin data classes for production applications:

1. Use annotation-based name mapping. Most REST APIs use snake_case while Kotlin conventions use camelCase. Always annotate properties with the appropriate library annotation:

// kotlinx.serialization
@SerialName("created_at") val createdAt: String

// Moshi
@Json(name = "created_at") val createdAt: String

// Gson
@SerializedName("created_at") val createdAt: String

2. Keep data classes immutable. Use val (read-only) properties instead of var. Immutable data classes are thread-safe, predictable, and work correctly with copy(). When you need to update a field, use copy(field = newValue) to create a new instance.

3. Validate at the boundary. Add validation in a companion object factory or an init block so invalid JSON data fails fast at deserialization time:

data class Product(
    val id: Long,
    val name: String,
    val price: Double
) {
    init {
        require(name.isNotBlank()) { "Product name cannot be blank" }
        require(price >= 0) { "Price must be non-negative" }
    }
}

4. Prefer kotlinx.serialization for new projects. It is the only library that is first-party Kotlin, supports KMP, avoids reflection, and fully respects null safety and default values.

5. Use sealed classes for polymorphic JSON. When a JSON response contains objects with a type discriminator field, model them as a Kotlin sealed class hierarchy. This gives you exhaustive when expressions and eliminates unsafe casting:

@Serializable
sealed class Notification {
    abstract val message: String
}

@Serializable @SerialName("email")
data class EmailNotification(
    override val message: String,
    val recipient: String
) : Notification()

@Serializable @SerialName("push")
data class PushNotification(
    override val message: String,
    val title: String
) : Notification()

// Exhaustive when (compiler enforces all branches)
when (notification) {
    is EmailNotification -> sendEmail(notification.recipient)
    is PushNotification -> showPush(notification.title)
}

6. Use extension functions for clean parsing. Kotlin extension functions let you add deserialization helpers to String or other types:

inline fun <reified T> String.parseJson(): T =
    Json { ignoreUnknownKeys = true }.decodeFromString(this)

// Usage
val user: User = jsonString.parseJson()
val orders: List<Order> = jsonArrayString.parseJson()

7. Handle API evolution gracefully. Always set ignoreUnknownKeys = true and give optional properties default values. This prevents deserialization failures when the backend adds new fields or removes deprecated ones.

Kotlin vs Java for JSON Handling

Kotlin offers significant advantages over Java when working with JSON APIs. Here is a side-by-side comparison:

FeatureKotlinJava
Null safetyCompile-time enforcement (String? vs String)Runtime only (@Nullable is advisory)
BoilerplateData class auto-generates equals/hashCode/toString/copyRequires Lombok, Records (16+), or manual code
Default valuesBuilt into constructor syntaxRequires builder pattern or overloaded constructors
Immutabilityval keyword, copy() for updatesManual final fields, no built-in copy
Extension functionsString.toUser() adds parsing to any typeNot available; use static utility methods
Sealed classesExhaustive when for polymorphic JSONSealed interfaces (Java 17+) with limited pattern matching
Multiplatformkotlinx.serialization works on JVM, JS, Native, WASMJVM only
Destructuringval (id, name) = userNot available

Kotlin data classes with kotlinx.serialization produce roughly 60-70% less code than equivalent Java POJOs with Jackson. The compile-time null safety alone prevents an entire category of runtime crashes that plague Java JSON parsing. If your project supports both languages, see our JSON to Java converter for generating Java models.

For TypeScript developers working on the frontend that consumes the same API, check out our JSON to TypeScript complete guide.

Frequently Asked Questions

What is the best library for JSON to Kotlin data class conversion?

For new projects, kotlinx.serialization is the recommended choice. It is maintained by JetBrains, uses compile-time code generation (no reflection), supports Kotlin Multiplatform, and natively understands Kotlin null safety and default values. Moshi is an excellent alternative for Android projects that already rely on Retrofit, as it also offers compile-time codegen and respects Kotlin nullability. Avoid Gson for new Kotlin projects because it was designed for Java and cannot enforce Kotlin null safety.

How do I handle nullable and optional fields when converting JSON to Kotlin?

Declare optional fields as nullable with a default value: val nickname: String? = null. With kotlinx.serialization, properties that have default values are automatically treated as optional during deserialization. With Moshi, the Kotlin codegen adapter handles nullable properties correctly. With Gson, always declare potentially absent fields as nullable because Gson will silently set them to null even if declared as non-nullable in Kotlin.

What is the difference between a Kotlin data class and a regular class for JSON?

A Kotlin data class automatically generates equals(), hashCode(), toString(), copy(), and componentN() (destructuring) functions based on its primary constructor parameters. This makes data classes ideal for JSON models because you get value-based equality, readable debug output, and immutable copying without writing any additional code. A regular class does not generate these functions. Use data class for all JSON models unless you need custom equality logic or class inheritance.

Can I convert JSON to Kotlin data class online without installing tools?

Yes. Our free online JSON to Kotlin converter at DevToolBox lets you paste any JSON and instantly generate Kotlin data classes with proper type inference, null safety annotations, and serialization annotations for kotlinx.serialization, Moshi, or Gson. No IDE plugin or CLI tool required.

How do I convert nested JSON arrays to Kotlin data classes?

For nested JSON arrays like {"users": [{"name": "Alice"}, {"name": "Bob"}]}, create a data class for the array element (data class User(val name: String)) and declare the field as val users: List<User> in the parent class. With kotlinx.serialization, this works automatically. With Moshi, use Types.newParameterizedType(List::class.java, User::class.java) to create the correct adapter. Our JSON to Kotlin tool handles nested arrays automatically.

Converting JSON to Kotlin data classes is a foundational skill for every Kotlin developer working with APIs. From kotlinx.serialization for Kotlin Multiplatform to Moshi for Android Retrofit integration, choosing the right library and following idiomatic patterns ensures type-safe, null-safe, and maintainable code. Use our free online JSON to Kotlin converter for instant data class generation, and refer to this guide whenever you need to handle nested objects, nullable fields, or polymorphic JSON structures.

Related Developer Tools and Guides

𝕏 Twitterin LinkedIn
บทความนี้มีประโยชน์ไหม?

อัปเดตข่าวสาร

รับเคล็ดลับการพัฒนาและเครื่องมือใหม่ทุกสัปดาห์

ไม่มีสแปม ยกเลิกได้ตลอดเวลา

ลองเครื่องมือที่เกี่ยวข้อง

KTJSON to KotlinJVJSON to Java ClassTSJSON to TypeScript{ }JSON Formatter

บทความที่เกี่ยวข้อง

ตัวแปลง JSON เป็นคลาส Java: คู่มือ POJO, Jackson, Gson และ Lombok

แปลง JSON เป็นคลาส Java ออนไลน์ เรียนรู้วิธีสร้าง POJO ด้วย Jackson, Gson และ Lombok พร้อมตัวอย่างโค้ด

JSON เป็น TypeScript: คู่มือฉบับสมบูรณ์พร้อมตัวอย่าง

เรียนรู้วิธีแปลงข้อมูล JSON เป็น interface ของ TypeScript โดยอัตโนมัติ ครอบคลุม nested objects, arrays, optional fields และแนวทางปฏิบัติที่ดี

JSON เป็น Dart: คู่มือ Model Class สำหรับ Flutter

เรียนรู้การแปลง JSON เป็นคลาส Dart model สำหรับ Flutter fromJson, toJson, null safety และ json_serializable