kotlinintermediate

Kotlinx Serialization — JSON Parsing

Serialize and deserialize JSON with kotlinx.serialization: data classes, custom serializers, and polymorphism.

kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class User(
    val id: Long,
    val name: String,
    val email: String,
    @SerialName("created_at") val createdAt: String = "",
    val roles: List<String> = emptyList(),
    val metadata: Map<String, String> = emptyMap()
)

@Serializable
data class ApiResponse<T>(
    val data: T,
    val status: Int = 200,
    val message: String = "OK"
)

// Polymorphic serialization
@Serializable
sealed class Shape {
    abstract fun area(): Double

    @Serializable
    @SerialName("circle")
    data class Circle(val radius: Double) : Shape() {
        override fun area() = Math.PI * radius * radius
    }

    @Serializable
    @SerialName("rectangle")
    data class Rectangle(val width: Double, val height: Double) : Shape() {
        override fun area() = width * height
    }
}

@Serializable
data class Drawing(val name: String, val shapes: List<Shape>)

fun main() {
    // Configure JSON
    val json = Json {
        prettyPrint = true
        ignoreUnknownKeys = true
        encodeDefaults = true
        isLenient = true
    }

    // Serialize
    val user = User(
        id = 1,
        name = "Alice",
        email = "alice@test.com",
        createdAt = "2024-01-15",
        roles = listOf("admin", "editor"),
        metadata = mapOf("theme" to "dark")
    )
    val jsonString = json.encodeToString(user)
    println("Serialized:\n$jsonString")

    // Deserialize
    val parsed = json.decodeFromString<User>(jsonString)
    println("\nDeserialized: $parsed")

    // Parse unknown JSON
    val rawJson = """{ "key": "value", "count": 42, "nested": { "a": 1 } }"""
    val element = Json.parseToJsonElement(rawJson)
    println("\nRaw key: ${element.jsonObject["key"]?.jsonPrimitive?.content}")
    println("Raw count: ${element.jsonObject["count"]?.jsonPrimitive?.int}")

    // Generic response
    val response = ApiResponse(data = user, status = 200)
    val responseJson = json.encodeToString(response)
    println("\nResponse:\n$responseJson")

    // List serialization
    val users = listOf(user, user.copy(id = 2, name = "Bob"))
    val listJson = json.encodeToString(users)
    val parsedList = json.decodeFromString<List<User>>(listJson)
    println("\nParsed ${parsedList.size} users")

    // Polymorphic
    val drawing = Drawing(
        "My Drawing",
        listOf(
            Shape.Circle(5.0),
            Shape.Rectangle(10.0, 20.0)
        )
    )
    val drawingJson = json.encodeToString(drawing)
    println("\nDrawing:\n$drawingJson")

    val parsedDrawing = json.decodeFromString<Drawing>(drawingJson)
    parsedDrawing.shapes.forEach { shape ->
        println("  ${shape::class.simpleName}: area = ${"%.2f".format(shape.area())}")
    }

    // Build JSON dynamically
    val dynamic = buildJsonObject {
        put("name", "Dynamic")
        put("version", 2)
        putJsonArray("tags") {
            add("kotlin")
            add("json")
        }
        putJsonObject("config") {
            put("debug", false)
        }
    }
    println("\nDynamic: ${json.encodeToString(dynamic)}")
}

Use Cases

  • API response parsing and generation
  • Configuration file serialization
  • Type-safe JSON handling

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.