Skip to main content
Version: 0.14

Default Values

Fory supports Kotlin data class default values during deserialization when using compatible mode. This feature enables forward/backward compatibility when data class schemas evolve.

Overview

When a Kotlin data class has parameters with default values, Fory can:

  1. Detect default values using Kotlin reflection
  2. Apply default values during deserialization when fields are missing from serialized data
  3. Support schema evolution by allowing new fields with defaults to be added without breaking existing serialized data

Usage

This feature is automatically enabled when:

  • Compatible mode is enabled (withCompatibleMode(CompatibleMode.COMPATIBLE))
  • Kotlin serializers are registered (KotlinSerializers.registerSerializers(fory))
  • A field is missing from the serialized data but exists in the target class with a default value

Example

import org.apache.fory.Fory
import org.apache.fory.config.CompatibleMode
import org.apache.fory.serializer.kotlin.KotlinSerializers

// Original data class
data class User(val name: String, val age: Int)

// Evolved data class with new field and default value
data class UserV2(val name: String, val age: Int, val email: String = "default@example.com")

fun main() {
val fory = Fory.builder()
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.build()
KotlinSerializers.registerSerializers(fory)
fory.register(User::class.java)
fory.register(UserV2::class.java)

// Serialize with old schema
val oldUser = User("John", 30)
val serialized = fory.serialize(oldUser)

// Deserialize with new schema - missing field gets default value
val newUser = fory.deserialize(serialized) as UserV2
println(newUser) // UserV2(name=John, age=30, email=default@example.com)
}

Supported Default Value Types

The following types are supported for default values:

  • Primitive types: Int, Long, Double, Float, Boolean, Byte, Short, Char
  • Unsigned types: UInt, ULong, UByte, UShort
  • String: String
  • Collections: List, Set, Map (with default instances)
  • Custom objects: Any object that can be instantiated via reflection

Complex Default Values

Default values can be complex expressions:

data class ConfigV1(val name: String)

data class ConfigV2(
val name: String,
val settings: Map<String, String> = mapOf("default" to "value"),
val tags: List<String> = listOf("default"),
val enabled: Boolean = true,
val retryCount: Int = 3
)

val fory = Fory.builder()
.withCompatibleMode(CompatibleMode.COMPATIBLE)
.build()
KotlinSerializers.registerSerializers(fory)

val original = ConfigV1("myConfig")
val serialized = fory.serialize(original)

val deserialized = fory.deserialize(serialized) as ConfigV2
// deserialized.name == "myConfig"
// deserialized.settings == mapOf("default" to "value")
// deserialized.tags == listOf("default")
// deserialized.enabled == true
// deserialized.retryCount == 3

Nullable Fields with Defaults

Nullable fields with default values are also supported:

data class PersonV1(val name: String)

data class PersonV2(
val name: String,
val nickname: String? = null,
val age: Int? = null
)

val original = PersonV1("John")
val serialized = fory.serialize(original)

val deserialized = fory.deserialize(serialized) as PersonV2
// deserialized.name == "John"
// deserialized.nickname == null (default)
// deserialized.age == null (default)