Free & open source — no account required
Free & open source — no account required
This technical guide provides an in-depth analysis of the json to kotlin class engine, best practices for implementation, and data security standards.
Kotlin's data classes are the natural fit for JSON mapping — they come with generated equals(), hashCode(), copy(), and toString() built into the language. Converting your JSON to Kotlin data classes unlocks two dominant serialization libraries: kotlinx.serialization (Kotlin-first, multiplatform-ready, compile-time code generation) and Moshi (Square's reflection-based library with Kotlin codegen adapter). For Android development, Retrofit + Moshi or Retrofit + kotlinx.serialization is the standard stack. For backend Kotlin with Ktor or Spring Boot, kotlinx.serialization integrates natively.
// Input JSON
{
"order_id": "ORD-772",
"status": "shipped",
"customer_name": "Sarah Jenkins",
"is_gift": false,
"items": [
{ "sku": "WGT-001", "quantity": 2, "unit_price": 49.99 }
],
"created_at": "2024-01-15T08:30:00Z"
}
// Generated Kotlin Data Classes
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
@Serializable
data class OrderItem(
val sku: String,
val quantity: Int,
@SerialName("unit_price")
val unitPrice: Double
)
@Serializable
data class Order(
@SerialName("order_id")
val orderId: String,
val status: OrderStatus,
@SerialName("customer_name")
val customerName: String,
@SerialName("is_gift")
val isGift: Boolean,
val items: List<OrderItem>,
@SerialName("created_at")
val createdAt: String // ISO 8601 string — use kotlinx-datetime for parsing
)
@Serializable
enum class OrderStatus {
@SerialName("pending") PENDING,
@SerialName("shipped") SHIPPED,
@SerialName("delivered") DELIVERED,
@SerialName("cancelled") CANCELLED
}
// Deserialize
import kotlinx.serialization.json.Json
val json = Json { ignoreUnknownKeys = true }
val order: Order = json.decodeFromString(jsonString)
// Serialize
val jsonOut: String = json.encodeToString(order)
@SerialName("snake_case_key") maps the JSON key to a Kotlin property name following Kotlin's camelCase convention. Using ignoreUnknownKeys = true in the Json config makes the model tolerant of extra fields in the API response — essential when consuming APIs that evolve over time.
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
@Serializable
data class UserProfile(
val id: String,
val username: String,
val email: String,
// Nullable — field is present but may be null
val bio: String? = null,
// Optional with default — field may be absent from JSON
val role: String = "user",
// Nullable optional — absent OR null → null
val deletedAt: String? = null,
// Nested nullable object
val address: Address? = null,
)
@Serializable
data class Address(
val street: String,
val city: String,
val country: String,
val postalCode: String? = null,
)
In Kotlin + kotlinx.serialization, a field with a default value is optional in JSON — if the key is absent, the default is used. A nullable type (T?) without a default requires the key to be present (even if null). To handle both absent and null, use val field: T? = null.
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.datetime.Instant
// Custom serializer for kotlinx-datetime Instant
object InstantSerializer : KSerializer<Instant> {
override val descriptor = PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Instant) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): Instant {
return Instant.parse(decoder.decodeString())
}
}
@Serializable
data class Event(
val id: String,
val name: String,
@Serializable(with = InstantSerializer::class)
val occurredAt: Instant,
)
// Custom serializer for sealed classes (discriminated unions)
@Serializable
sealed class ApiEvent {
@Serializable
@SerialName("click")
data class Click(val x: Int, val y: Int) : ApiEvent()
@Serializable
@SerialName("keypress")
data class Keypress(val key: String, val ctrl: Boolean) : ApiEvent()
}
// Deserializes {"type": "click", "x": 100, "y": 200} automatically
// kotlinx.serialization reads the "type" field as discriminator
// build.gradle.kts (app)
dependencies {
implementation("com.squareup.moshi:moshi:1.15.0")
implementation("com.squareup.moshi:moshi-kotlin:1.15.0")
ksp("com.squareup.moshi:moshi-kotlin-codegen:1.15.0")
}
// Model with Moshi
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true) // triggers codegen
data class Product(
val id: String,
val name: String,
@Json(name = "unit_price")
val unitPrice: Double,
val inStock: Boolean,
val tags: List<String> = emptyList(),
)
// Moshi instance with Kotlin adapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
val moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.build()
val adapter = moshi.adapter(Product::class.java)
val product: Product? = adapter.fromJson(jsonString)
val json: String = adapter.toJson(product)
// API interface with kotlinx.serialization converter
import retrofit2.Retrofit
import retrofit2.http.*
import kotlinx.serialization.Serializable
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import okhttp3.MediaType.Companion.toMediaType
@Serializable
data class UserResponse(
val id: String,
val username: String,
val email: String,
)
interface UserApi {
@GET("users/{id}")
suspend fun getUser(@Path("id") id: String): UserResponse
@POST("users")
suspend fun createUser(@Body request: CreateUserRequest): UserResponse
@GET("users")
suspend fun listUsers(
@Query("page") page: Int = 1,
@Query("limit") limit: Int = 20,
): List<UserResponse>
}
// Retrofit setup
val json = Json { ignoreUnknownKeys = true; isLenient = true }
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
val api = retrofit.create(UserApi::class.java)
// Ktor server with kotlinx.serialization
import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.plugins.contentnegotiation.*
fun Application.configureRouting() {
install(ContentNegotiation) {
json(Json { prettyPrint = false; ignoreUnknownKeys = true })
}
routing {
post("/users") {
val request = call.receive<CreateUserRequest>() // auto-deserialized
val user = userService.create(request)
call.respond(user) // auto-serialized
}
}
}
ignoreUnknownKeys = true globally: Set this on your Json instance. API responses evolve — unknown fields should not crash your app.Order instances with the same field values are equal with ==, which is critical for correct state management in Android ViewModels.sealed class ApiEvent with @SerialName on each subclass provides exhaustive when expression handling — the compiler forces you to handle every event type.copy() for immutable updates: Never mutate JSON model fields (use val). Create updated versions with order.copy(status = OrderStatus.DELIVERED) — this is the idiomatic Kotlin pattern for state updates.Q: kotlinx.serialization vs. Moshi — which should I use?
A: kotlinx.serialization is the recommended choice for new projects. It supports Kotlin Multiplatform (shared code across Android/iOS/JVM), uses compile-time code generation (no reflection at runtime), and integrates natively with Ktor. Moshi is excellent for Android-only projects or teams already invested in Square's OkHttp/Retrofit ecosystem — its codegen adapter is well-tested and battle-hardened.
Q: How do I parse ISO 8601 dates in Kotlin?
A: Use kotlinx-datetime (implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")) for multiplatform datetime types. Register a custom serializer or use @Contextual with a serializer registered on the Json instance. For JVM-only, java.time.Instant with a custom serializer works too.
Q: What is the Kotlin equivalent of TypeScript's Record<string, unknown>?
A: Use Map<String, Any?> with JsonObject from kotlinx.serialization for structured access, or kotlinx.serialization.json.JsonElement for a typed JSON tree that can be traversed without a concrete data class.
Q: How do I handle a JSON field named the same as a Kotlin keyword?
A: Use @SerialName("type") with a renamed property: @SerialName("type") val kind: String. The JSON key is "type"; the Kotlin property is kind.
Is the processing local-only?
Absolutely. TypeMorph operates entirely within your browser's sandbox. We use Web Workers for high-performance computation without ever transmitting your JSON, SQL, or API data to a remote server.
Can I use this for enterprise projects?
Yes. The tool is designed for professional software engineers who require GDPR compliance and data privacy. It is trusted by developers at top-tier startups and financial institutions.