# JSON to Kotlin Data Class Conversion: Efficient Strategies for Android Development

> Streamline JSON to Kotlin data class conversion in Android with kotlinx-serialization. Generate type-safe, zero-reflection serializers efficiently using the @Serializable annotation for seamless integration.

- Repository: [JetBrains/kotlin](https://github.com/jetbrains/kotlin)
- Tags: how-to-guide
- Published: 2026-02-16

---

**The most efficient approach to JSON to Kotlin data class conversion leverages the kotlinx-serialization compiler plugin, which generates type-safe, zero-reflection serializers at compile time via the `@Serializable` annotation.**

Converting complex JSON payloads into type-safe Kotlin data classes is a critical task in modern Android development. The JetBrains/kotlin repository provides the kotlinx-serialization compiler plugin, which automates JSON to Kotlin data class conversion through compile-time code generation, eliminating the performance penalties of reflection-based parsing.

## Understanding Compile-Time JSON to Kotlin Data Class Conversion

The Kotlin compiler ships with the **kotlinx-serialization** compiler plugin located in `plugins/kotlinx-serialization/`. When a class is annotated with `@Serializable`, the plugin generates a **serializer** (`<Class>.serializer()` and, for performance-critical code, `<Class>.generatedSerializer()`). These serializers are ordinary implementations of `kotlinx.serialization.KSerializer<T>` that know how to read/write JSON using the **`kotlinx.serialization.json.Json`** runtime.

The generated code is inserted into the **IR/backend** pipeline (see [`plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt`](https://github.com/JetBrains/kotlin/blob/main/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt)), so at runtime the `Json` instance can invoke the appropriate serializer without reflection.

Key architectural components include:

- **[`SerializationFirResolveExtension.kt`](https://github.com/JetBrains/kotlin/blob/main/SerializationFirResolveExtension.kt)** – Registers generated serializers during FIR resolution in the compiler frontend.
- **[`SerializableIrGenerator.kt`](https://github.com/JetBrains/kotlin/blob/main/SerializableIrGenerator.kt)** – Emits the actual byte-code for serializer classes in the IR/backend phase.
- **[`NamingConventions.kt`](https://github.com/JetBrains/kotlin/blob/main/NamingConventions.kt)** – Defines the `generatedSerializer()` naming convention and lookup logic used by [`SerializerSearchUtil.kt`](https://github.com/JetBrains/kotlin/blob/main/SerializerSearchUtil.kt).
- **`Json`** – The runtime class that delegates to generated serializers for actual parsing.

Because the serializers are **compile-time generated**, JSON to Kotlin data class conversion is fast, type-safe, and works seamlessly on Android without ProGuard or R8 issues.

## Implementing JSON to Kotlin Data Class Conversion in Practice

### Define Your Data Model with @Serializable

Start by annotating your data classes with `@Serializable`. The compiler plugin will automatically generate the serializer code.

```kotlin
@Serializable
data class User(
    val id: Long,
    val name: String,
    val address: Address,
    val roles: List<Role>,
    @SerialName("created_at") val createdAt: String
)

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

@Serializable
enum class Role { ADMIN, USER, GUEST }

```

The `@Serializable` annotation triggers the plugin to generate `User.serializer()` and `User.generatedSerializer()`, as demonstrated in the test fixture [`plugins/kotlinx-serialization/testData/firMembers/serializableWithCompanion.kt`](https://github.com/JetBrains/kotlin/blob/main/plugins/kotlinx-serialization/testData/firMembers/serializableWithCompanion.kt).

### Configure the Json Instance for Android Payloads

Create a configured `Json` instance to handle real-world API responses that may contain unknown keys or require lenient parsing.

```kotlin
val json = Json {
    ignoreUnknownKeys = true          // Tolerant of extra fields from the server
    isLenient = true                 // Allows non-quoted keys and unquoted strings
    encodeDefaults = false           // Omit default values when encoding
    explicitNulls = false            // Skip null fields in output
    prettyPrint = true               // Useful for debugging
}

```

This configuration pattern is validated in the repository's test data at [`plugins/kotlinx-serialization/testData/firMembers/privatePropertiesSerialization.kt`](https://github.com/JetBrains/kotlin/blob/main/plugins/kotlinx-serialization/testData/firMembers/privatePropertiesSerialization.kt).

### Decode JSON Using Generated Serializers

For the fastest performance on Android, use the generated serializer directly to avoid any reflective lookups.

```kotlin
// Standard approach
val user: User = json.decodeFromString(User.serializer(), jsonString)

// Fastest approach - uses compile-time generated code with no reflection
val userFast: User = json.decodeFromString(User.generatedSerializer(), jsonString)

```

The `generatedSerializer()` method is defined according to the naming conventions in [`plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt`](https://github.com/JetBrains/kotlin/blob/main/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt).

## Advanced Patterns for Complex JSON Structures

### Handling Sealed Class Hierarchies

For polymorphic JSON structures, use sealed classes with a custom class discriminator.

```kotlin
@Serializable
sealed class Shape {
    @Serializable 
    data class Circle(val radius: Double) : Shape()
    
    @Serializable 
    data class Rectangle(val width: Double, val height: Double) : Shape()
}

// Configure discriminator field name
val json = Json { classDiscriminator = "type" }

// Encode a list of mixed shapes
val shapes = listOf(Shape.Circle(1.5), Shape.Rectangle(2.0, 3.0))
val payload = json.encodeToString(ListSerializer(Shape.serializer()), shapes)

// Decode with generated serializers
val decoded = json.decodeFromString(
    ListSerializer(Shape.generatedSerializer()),
    payload
)

```

## Key Source Files in the JetBrains/kotlin Repository

Understanding the implementation details helps optimize your JSON to Kotlin data class conversion strategy.

| File (relative to repo root) | Role in JSON Conversion |
|-------------------------------|-------------------------|
| [`plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt`](https://github.com/JetBrains/kotlin/blob/main/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt) | Registers generated serializers during FIR resolution phase. |
| [`plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt`](https://github.com/JetBrains/kotlin/blob/main/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializableIrGenerator.kt) | Emits byte-code for serializer classes in the IR/backend pipeline. |
| [`plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt`](https://github.com/JetBrains/kotlin/blob/main/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt) | Defines `generatedSerializer()` naming and lookup conventions. |
| [`plugins/kotlinx-serialization/testData/firMembers/serializableWithCompanion.kt`](https://github.com/JetBrains/kotlin/blob/main/plugins/kotlinx-serialization/testData/firMembers/serializableWithCompanion.kt) | Demonstrates basic encoding/decoding patterns with `Json.encodeToString`. |
| [`plugins/kotlinx-serialization/testData/firMembers/privatePropertiesSerialization.kt`](https://github.com/JetBrains/kotlin/blob/main/plugins/kotlinx-serialization/testData/firMembers/privatePropertiesSerialization.kt) | Shows `Json` configuration options like `encodeDefaults`. |
| [`libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/jsonUtils.kt`](https://github.com/JetBrains/kotlin/blob/main/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/jsonUtils.kt) | Gradle plugin utilities for JSON metadata handling. |
| [`kotlin-native/performance/reports/src/main/kotlin/report/json/JsonElement.kt`](https://github.com/JetBrains/kotlin/blob/main/kotlin-native/performance/reports/src/main/kotlin/report/json/JsonElement.kt) | Internal JSON DOM implementation for performance tooling. |

## Android Integration Example

Here is a complete example integrating JSON to Kotlin data class conversion into an Android networking layer using OkHttp and Kotlin coroutines.

```kotlin
// File: app/src/main/java/com/example/network/ApiService.kt
class ApiService(private val client: OkHttpClient) {

    private val json = Json { 
        ignoreUnknownKeys = true 
        isLenient = true 
    }

    fun fetchUser(id: Long): Flow<User> = flow {
        val request = Request.Builder()
            .url("https://api.example.com/users/$id")
            .build()
            
        client.newCall(request).execute().use { response ->
            if (!response.isSuccessful) throw IOException("HTTP ${response.code}")
            val body = response.body?.string() ?: throw IOException("Empty body")
            
            // Fast deserialization using compile-time generated serializer
            val user = json.decodeFromString(User.generatedSerializer(), body)
            emit(user)
        }
    }
}

```

The `User` data class uses `@Serializable` as defined earlier. By calling `User.generatedSerializer()`, the Android runtime avoids reflection entirely, ensuring optimal performance on resource-constrained devices and compatibility with R8 code shrinking.

## Summary

- **kotlinx-serialization** provides the most efficient JSON to Kotlin data class conversion through compile-time code generation, eliminating reflection overhead critical for Android performance.
- The compiler plugin operates in two phases: FIR resolution ([`SerializationFirResolveExtension.kt`](https://github.com/JetBrains/kotlin/blob/main/SerializationFirResolveExtension.kt)) and IR generation ([`SerializableIrGenerator.kt`](https://github.com/JetBrains/kotlin/blob/main/SerializableIrGenerator.kt)) to produce serializer implementations.
- Use `@Serializable` annotations on data classes, then configure a `Json` instance with options like `ignoreUnknownKeys` and `isLenient` to handle real-world API responses.
- For maximum performance, invoke `generatedSerializer()` directly rather than `serializer()` to bypass any reflective lookup mechanisms.
- The generated serializers integrate seamlessly with Android networking libraries like OkHttp and Retrofit, providing type-safe parsing without ProGuard configuration issues.

## Frequently Asked Questions

### What is the fastest method for JSON to Kotlin data class conversion in Android?

The fastest method uses **kotlinx-serialization** with the `generatedSerializer()` function. According to the [`NamingConventions.kt`](https://github.com/JetBrains/kotlin/blob/main/NamingConventions.kt) file in the JetBrains/kotlin repository, this approach returns the compile-time generated serializer instance directly without reflection. This is critical for Android where reflection impacts startup time and increases APK size.

### How does kotlinx-serialization handle unknown JSON keys during conversion?

The `Json` builder provides the `ignoreUnknownKeys` configuration option. When set to `true`, the generated serializer (created by [`SerializableIrGenerator.kt`](https://github.com/JetBrains/kotlin/blob/main/SerializableIrGenerator.kt)) skips fields present in the JSON but missing from the Kotlin data class definition. This is demonstrated in the repository's test data at [`privatePropertiesSerialization.kt`](https://github.com/JetBrains/kotlin/blob/main/privatePropertiesSerialization.kt).

### Can I use kotlinx-serialization with Retrofit for Android networking?

Yes. While Retrofit traditionally uses Gson or Moshi converters, you can integrate kotlinx-serialization by using the `kotlinx-serialization-converter` dependency. This allows Retrofit to use the same `Json` instance and generated serializers (`User.serializer()` or `User.generatedSerializer()`) shown in the [`serializableWithCompanion.kt`](https://github.com/JetBrains/kotlin/blob/main/serializableWithCompanion.kt) test fixtures, ensuring consistent performance across your Android application.

### What is the difference between serializer() and generatedSerializer()?

The `serializer()` function is the standard entry point that may involve lookup logic, while `generatedSerializer()` provides direct access to the compile-time generated instance. As defined in [`NamingConventions.kt`](https://github.com/JetBrains/kotlin/blob/main/NamingConventions.kt) and implemented in the IR generation phase ([`SerializableIrGenerator.kt`](https://github.com/JetBrains/kotlin/blob/main/SerializableIrGenerator.kt)), `generatedSerializer()` avoids any reflective overhead, making it the preferred choice for high-performance Android applications where every millisecond of startup time matters.