Implementing Data Serialization
1. Using Parcelable Interface
The Parcelable interface in Android is used for efficiently serializing and deserializing objects, particularly useful when passing data between activities or services. It's more efficient than Serializable for Android-specific use cases.
| Property | Description | Notes |
|---|---|---|
| writeToParcel(Parcel, Int) | Writes the object's data to a Parcel. |
Must be implemented to define how the object is serialized. |
| describeContents() | Returns the content description of the object. | Typically returns 0 unless there are special content types. |
| CREATOR | A Parcelable.Creator instance used to recreate the object from a Parcel. |
Must be implemented as a static field. |
Example: Implementing Parcelable
import android.os.Parcel
import android.os.Parcelable
data class User(
val name: String,
val age: Int,
val email: String
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readString() ?: "",
parcel.readInt(),
parcel.readString() ?: ""
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
parcel.writeInt(age)
parcel.writeString(email)
}
override fun describeContents(): Int = 0
companion object CREATOR : Parcelable.Creator<User> {
override fun createFromParcel(parcel: Parcel): User {
return User(parcel)
}
override fun newArray(size: Int): Array<User?> {
return arrayOfNulls(size)
}
}
}
Parcelable interface requires a special constructor that reads from a Parcel, and the CREATOR object must be implemented to support reconstruction of the object.
Parcelable is Android-specific and not suitable for cross-platform or general Java use. For generic serialization, consider using Serializable or libraries like Gson or Jackson.
2. Implementing Parcelable with @Parcelize
Example: Basic Parcelable Implementation
@Parcelize
data class User(
val id: Int,
val name: String,
val email: String
) : Parcelable
| Property | Type | Description |
|---|---|---|
| @Parcelize | Annotation |
Marker annotation to automatically generate Parcelable implementation |
| data class | Class Declaration |
Required for @Parcelize to work; must contain only immutable properties |
| Parcelable | Interface |
Marker interface indicating the class can be serialized |
Supported Types
StringInt,Long,Float,DoubleBooleanChar,Byte,ShortArray<T>where T is supportedList<T>where T is supportedMap<K, V>where K and V are supportedParcelableobjects
3. Using Serializable Interface
The Serializable interface in Kotlin is a marker interface that enables objects to be converted into a byte stream for persistence or transmission. It's particularly useful for saving and restoring object states, such as in Android's Bundle or when passing data between activities.
| Property | Description | Notes |
|---|---|---|
| serialVersionUID | A version UID to ensure compatibility during deserialization. | Recommended for explicit control over serialization versions. |
Example: Implementing Serializable in a Data Class
import java.io.Serializable
data class User(
val name: String,
val age: Int,
val email: String
) : Serializable
Serializable is convenient, it's less efficient than alternatives like Parcelable for Android development. For better performance, consider using Parcelable.
Serializable, ensure that all fields in the class are serializable. Custom objects must also implement Serializable.
4. Working with JSON
| Method | Description | Return Type | Notes |
|---|---|---|---|
| Json.encodeToString() | Serializes a Kotlin object to JSON string | String |
Requires kotlinx.serialization dependency |
| Json.decodeFromString() | Deserializes a JSON string to Kotlin object | T |
Requires kotlinx.serialization dependency |
Example: Serializing and Deserializing JSON
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class User(
val id: Int,
val name: String,
val email: String
)
val user = User(1, "Alice", "alice@example.com")
val json = Json.encodeToString(user)
println(json) // {"id":1,"name":"Alice","email":"alice@example.com"}
val deserializedUser = Json.decodeFromString<User>(json)
println(deserializedUser) // User(id=1, name=Alice, email=alice@example.com)
@Serializable annotation is required for Kotlinx Serialization to work.
@Transient to avoid runtime errors.
| Feature | Description | Support |
|---|---|---|
| Null Safety | Handles null values gracefully in JSON | Good |
| Type Safety | Compile-time type checking for JSON structures | Good |
| Custom Serializers | Ability to define custom serialization logic | Modern |
5. Parsing JSON to Objects
| Property | Description | Example |
|---|---|---|
| JsonParser | Used to parse JSON strings into Kotlin objects | val parser = Json { ignoreUnknownKeys = true } |
| decodeFromString | Method to parse JSON string into a typed object | val user = parser.decodeFromString<User>(jsonString) |
Example: Parsing JSON to User Object
import kotlinx.serialization.json.*
data class User(
val id: Int,
val name: String,
val email: String
)
val jsonString = """
{
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
"""
val user = Json.decodeFromString<User>(jsonString)
println(user)
ignoreUnknownKeys = true to skip unknown fields.
6. Serializing Objects to JSON
| Property | Type | Description | Default |
|---|---|---|---|
| json | String |
Serialized JSON string representation of the object | null |
| serializer | KSerializer<T> |
Serializer instance for the type T |
null |
Example: Serializing a Kotlin object to JSON
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class User(
val id: Int,
val name: String,
val email: String
)
fun main() {
val user = User(1, "John Doe", "john@example.com")
val json = Json.encodeToString(user)
println(json) // {"id":1,"name":"John Doe","email":"john@example.com"}
}
Example: Using custom JSON configuration
import kotlinx.serialization.*
import kotlinx.serialization.json.*
val customJson = Json {
isLenient = true
ignoreUnknownKeys = true
encodeDefaults = false
}
@Serializable
data class Product(
val id: Int,
val name: String,
val price: Double = 0.0
)
fun main() {
val product = Product(101, "Laptop")
val json = customJson.encodeToString(product)
println(json) // {"id":101,"name":"Laptop"}
}
@Serializable annotation is required for Kotlin serialization to work with data classes.
encodeDefaults = false, fields with default values will not be included in the serialized JSON.
7. Using Gson Library
| Feature | Description | Code Example |
|---|---|---|
| Gson | JSON serialization/deserialization library for Java/Kotlin | |
| JsonElement | Represents a JSON element in the Gson library | |
Key Features of Gson
- Easy serialization/deserialization of Java/Kotlin objects to/from JSON
- Supports custom serializers and deserializers
- Handles complex nested objects
8. Using Moshi Library
Example: Basic JSON Serialization with Moshi
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
data class User(
val id: Int,
val name: String,
val email: String
)
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val adapter = moshi.adapter(User::class.java)
val user = User(1, "John Doe", "john@example.com")
val json = adapter.toJson(user)
println(json) // {"id":1,"name":"John Doe","email":"john@example.com"}
val userFromJson = adapter.fromJson(json)
println(userFromJson) // User(id=1, name=John Doe, email=john@example.com)
| Feature | Description | Notes |
|---|---|---|
| Moshi.Builder() | Creates a new Moshi instance | Must be configured with appropriate adapters |
| add(KotlinJsonAdapterFactory()) | Enables Kotlin-specific features | Required for Kotlin data classes |
| adapter(T::class.java) | Creates an adapter for a type | Used to serialize/deserialize objects |
KotlinJsonAdapterFactory to properly handle Kotlin data classes and nullability.KotlinJsonAdapterFactory is not added, Moshi will not correctly handle Kotlin-specific features like nullable properties.Example: Custom Adapter for Date
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonClass
import java.text.SimpleDateFormat
import java.util.*
@JsonClass(generateAdapter = true)
data class Event(
val id: Int,
val title: String,
@Json(name = "event_date")
val eventDate: Date
)
val dateFormatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val moshi = Moshi.Builder()
.add(Date::class.java, dateFormatter)
.build()
val adapter = moshi.adapter(Event::class.java)
val event = Event(1, "Android Dev Summit", Date())
val json = adapter.toJson(event)
println(json) // {"id":1,"title":"Android Dev Summit","event_date":"2026-04-05"}
9. Using Kotlinx Serialization
Kotlinx Serialization is a powerful library for serializing and deserializing data in Kotlin, particularly useful for converting objects to and from formats like JSON. It supports multiple formats and provides type-safe serialization.
| Feature | Description | Notes |
|---|---|---|
| @Serializable | Annotation to mark classes for serialization | Must be applied to data classes or classes with serializable properties |
| @SerialName | Customizes the serialized name of a property | Useful for mapping between different naming conventions |
| @EncodeDefault | Ensures default values are serialized | Prevents omission of properties with default values |
| @Ignore | Excludes a property from serialization | Useful for sensitive or computed properties |
Example: Basic Serialization
@Serializable
data class User(
val id: Int,
@SerialName("full_name") val name: String,
val email: String
)
val user = User(1, "John Doe", "john@example.com")
val json = Json.encodeToString(user)
println(json) // {"id":1,"full_name":"John Doe","email":"john@example.com"}
Example: Deserialization
val json = """{"id":1,"full_name":"John Doe","email":"john@example.com"}"""
val user = Json.decodeFromString<User>(json)
println(user) // User(id=1, name=John Doe, email=john@example.com)
@SerialName annotation is useful when the property name in the serialized format differs from the Kotlin property name.
val or var, and that they support serialization (e.g., basic types, collections, or other serializable classes).
| Format | Support | Notes |
|---|---|---|
| JSON | NEW Supported | Default format provided by the library |
| CBOR | EXPERIMENTAL Supported | Can be enabled with the kotlinx-serialization-cbor dependency |
| Protobuf | DEPRECATED Supported in older versions | Use kotlinx-serialization-protobuf for older versions |
10. Handling Null Values in JSON
| Property | Description | Default Behavior | Handling Strategy |
|---|---|---|---|
| @SerializedName | Defines the JSON field name for a property | Uses Kotlin property name | Use null to skip serialization |
| @JsonNull | Explicitly marks a field as null in JSON | Skips null fields by default | Use null in Kotlin to represent JSON null |
| @JsonAdapter | Customizes serialization for specific types | No custom handling | Implement JsonSerializer to control nulls |
Example: Serializing Null Values
import com.google.gson.annotations.SerializedName
data class User(
@SerializedName("name") val name: String?,
@SerializedName("email") val email: String? = null,
@SerializedName("age") val age: Int? = null
)
val user = User(name = "John Doe", email = null, age = 30)
Example: Custom Null Handling
import com.google.gson.*
class NullHandlingSerializer : JsonSerializer<String?> {
override fun serialize(
src: String?,
typeOfSrc: Type,
context: JsonSerializationContext
): JsonElement {
return if (src == null) JsonNull.INSTANCE else JsonPrimitive(src)
}
}
val gson = GsonBuilder()
.registerTypeAdapter(String::class.java, NullHandlingSerializer())
.create()
@SerializedName or custom serializers to control how nulls are handled in JSON output.