Skip to main content

Kotlin Serialization

Introduction

When building applications, you'll often need to convert Kotlin objects into a format that can be saved to a file or sent over a network, and then convert that format back into Kotlin objects. This process is called serialization (converting objects to a transferable format) and deserialization (converting the format back to objects).

Kotlin Serialization is the official library provided by JetBrains that makes this process straightforward and type-safe. It works particularly well with Kotlin's language features like null safety and data classes.

In this tutorial, we'll explore:

  • What serialization is and why it's useful
  • Setting up Kotlin Serialization in your project
  • Basic serialization and deserialization with JSON
  • Working with complex data structures
  • Handling custom serialization needs

Setting Up Kotlin Serialization

To use Kotlin Serialization, we need to add the library to our project.

Gradle Setup

Add the serialization plugin and dependency to your Gradle build files:

In your project-level build.gradle.kts:

kotlin
plugins {
kotlin("jvm") version "1.8.0"
kotlin("plugin.serialization") version "1.8.0"
}

In your module-level build.gradle.kts:

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

Basic JSON Serialization

Let's start with a simple example of serializing a Kotlin object to JSON and back.

Serializing a Simple Data Class

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

@Serializable
data class Person(val name: String, val age: Int)

fun main() {
val person = Person("Alice", 29)

// Convert object to JSON string
val jsonString = Json.encodeToString(person)
println("Serialized JSON: $jsonString")

// Convert JSON string back to object
val deserializedPerson = Json.decodeFromString<Person>(jsonString)
println("Deserialized object: $deserializedPerson")
}

Output:

Serialized JSON: {"name":"Alice","age":29}
Deserialized object: Person(name=Alice, age=29)

Let's break this down:

  1. We mark our Person class with @Serializable annotation to tell the Kotlin serialization library that this class can be serialized.
  2. We use Json.encodeToString() to convert our object to a JSON string.
  3. We use Json.decodeFromString<Person>() to convert the JSON string back to a Person object.

Working with Complex Data Structures

Nested Objects

Let's look at how to handle nested objects:

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

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

@Serializable
data class Employee(val name: String, val address: Address, val skills: List<String>)

fun main() {
val employee = Employee(
"Bob Johnson",
Address("123 Work St", "Techville", "12345"),
listOf("Kotlin", "Android", "Web Development")
)

val jsonString = Json.encodeToString(employee)
println("Employee JSON: $jsonString")

val deserializedEmployee = Json.decodeFromString<Employee>(jsonString)
println("City: ${deserializedEmployee.address.city}")
println("First skill: ${deserializedEmployee.skills.first()}")
}

Output:

Employee JSON: {"name":"Bob Johnson","address":{"street":"123 Work St","city":"Techville","postalCode":"12345"},"skills":["Kotlin","Android","Web Development"]}
City: Techville
First skill: Kotlin

Handling Collections

Kotlin Serialization can easily handle collections like List, Set, and Map:

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

@Serializable
data class Project(val name: String, val contributors: Map<String, List<String>>)

fun main() {
val project = Project(
"KotlinApp",
mapOf(
"Backend" to listOf("Alice", "Bob"),
"Frontend" to listOf("Charlie", "David", "Eve")
)
)

val jsonString = Json.encodeToString(project)
println("Project JSON: $jsonString")

val deserializedProject = Json.decodeFromString<Project>(jsonString)
println("Frontend team: ${deserializedProject.contributors["Frontend"]?.joinToString()}")
}

Output:

Project JSON: {"name":"KotlinApp","contributors":{"Backend":["Alice","Bob"],"Frontend":["Charlie","David","Eve"]}}
Frontend team: Charlie, David, Eve

Customizing Serialization

Ignoring Properties

Sometimes you have properties that should not be included in serialization:

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

@Serializable
data class User(
val username: String,
@Transient val temporaryToken: String = "", // This will be ignored
val email: String
)

fun main() {
val user = User("user123", "abc123token", "[email protected]")

val jsonString = Json.encodeToString(user)
println("User JSON (token should be missing): $jsonString")

val deserializedUser = Json.decodeFromString<User>(jsonString)
println(deserializedUser)
}

Output:

User JSON (token should be missing): {"username":"user123","email":"[email protected]"}
User(username=user123, temporaryToken=, [email protected])

Notice how the temporaryToken is not included in the JSON, and when deserializing, it gets the default empty string value.

Alternative Property Names

You can customize the property names in the serialized format:

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

@Serializable
data class Product(
@SerialName("product_id") val id: String,
@SerialName("product_name") val name: String,
@SerialName("price_usd") val price: Double
)

fun main() {
val product = Product("A123", "Wireless Headphones", 79.99)

val jsonString = Json.encodeToString(product)
println("Product with custom property names: $jsonString")
}

Output:

Product with custom property names: {"product_id":"A123","product_name":"Wireless Headphones","price_usd":79.99}

Real-World Application: API Communication

One common use case for serialization is communicating with APIs. Here's how you might use Kotlin Serialization to handle API responses:

kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.concurrent.thread

// Define data classes that match API response structure
@Serializable
data class WeatherData(
val location: String,
val temperature: Double,
val conditions: String,
val forecast: List<ForecastDay>
)

@Serializable
data class ForecastDay(
val day: String,
val temperatureHigh: Double,
val temperatureLow: Double
)

// Simulation of an API call
fun fetchWeatherData(): String {
// This would be a network call in a real app
return """
{
"location": "New York",
"temperature": 72.5,
"conditions": "Partly Cloudy",
"forecast": [
{"day": "Tuesday", "temperatureHigh": 75.0, "temperatureLow": 65.0},
{"day": "Wednesday", "temperatureHigh": 80.0, "temperatureLow": 68.0},
{"day": "Thursday", "temperatureHigh": 76.0, "temperatureLow": 63.0}
]
}
""".trimIndent()
}

fun main() {
// In a real app, this would happen in a coroutine or background thread
thread {
try {
val jsonResponse = fetchWeatherData()

// Parse the JSON response into our data class
val weatherData = Json.decodeFromString<WeatherData>(jsonResponse)

// Use the data
println("Current weather in ${weatherData.location}: ${weatherData.temperature}°F, ${weatherData.conditions}")
println("3-Day Forecast:")
weatherData.forecast.forEach { day ->
println(" ${day.day}: ${day.temperatureLow}°F - ${day.temperatureHigh}°F")
}
} catch (e: SerializationException) {
println("Error parsing weather data: ${e.message}")
}
}

// Give the thread time to complete
Thread.sleep(100)
}

Output:

Current weather in New York: 72.5°F, Partly Cloudy
3-Day Forecast:
Tuesday: 65.0°F - 75.0°F
Wednesday: 68.0°F - 80.0°F
Thursday: 63.0°F - 76.0°F

Configuring JSON Format

You can customize how JSON is formatted and parsed:

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

@Serializable
data class ConfigExample(
val id: Int,
val name: String,
val tags: List<String>? = null
)

fun main() {
val example = ConfigExample(1, "Example", listOf("test", "sample"))

// Create a custom JSON configuration
val customJson = Json {
prettyPrint = true // Add indentation and line breaks
ignoreUnknownKeys = true // Don't fail when unknown keys are in the JSON
coerceInputValues = true // Try to convert values to expected types
encodeDefaults = true // Include default values in serialization
}

val jsonString = customJson.encodeToString(example)
println("Pretty printed JSON:")
println(jsonString)
}

Output:

Pretty printed JSON:
{
"id": 1,
"name": "Example",
"tags": [
"test",
"sample"
]
}

Summary

Kotlin Serialization provides a powerful and type-safe way to convert Kotlin objects to various formats (primarily JSON) and back. Key points we've covered:

  • Setting up the Kotlin Serialization library in your project
  • Basic serialization and deserialization with the @Serializable annotation
  • Working with complex data structures including nested objects and collections
  • Customizing serialization behavior with annotations like @Transient and @SerialName
  • Configuring the JSON format with options like prettyPrint
  • Using serialization in real-world scenarios like API communication

The type safety that Kotlin Serialization provides helps catch potential errors at compile time rather than runtime, making your code more robust.

Further Resources and Exercises

Resources

Exercises

  1. Simple Serialization: Create a Book class with properties like title, author, and yearPublished. Serialize it to JSON and deserialize it back.

  2. Complex Object: Create a Library class that contains a list of Book objects and metadata like name and location. Serialize and deserialize the complete structure.

  3. Custom Formatting: Experiment with different JSON configuration options. Try to create a version that skips null values and another that includes default values.

  4. File Storage: Extend the Library example to save the serialized JSON to a file and then read it back.

  5. API Response: Practice with a real API by creating corresponding data classes for a public API of your choice, then fetch and deserialize the response.

By mastering Kotlin Serialization, you'll have a powerful tool for handling data persistence and network communication in your Kotlin applications.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)