Skip to main content
Version: 1.0

Default Values

Fory supports Scala class default values during native-mode deserialization when compatible mode is enabled. This feature enables forward/backward compatibility when case classes or regular Scala classes have default parameters.

Overview​

When a Scala class has default parameters, the Scala compiler generates methods in the companion object (for case classes) or in the class itself (for regular Scala classes) like apply$default$1, apply$default$2, etc. that return the default values. Fory can detect these methods and use them when deserializing objects where certain fields are missing from the serialized data.

Supported Class Types​

Fory supports default values for:

  • Case classes with default parameters
  • Regular Scala classes with default parameters in their primary constructor
  • Nested case classes with default parameters

How It Works​

  1. Detection: Fory detects if a class is a Scala class by checking for the presence of default value methods (apply$default$N or $default$N).

  2. Default Value Discovery:

    • For case classes: Fory scans the companion object for methods named apply$default$1, apply$default$2, etc.
    • For regular Scala classes: Fory scans the class itself for methods named $default$1, $default$2, etc.
  3. Field Mapping: During deserialization, Fory identifies fields that exist in the target class but are missing from the serialized data.

  4. Value Application: After reading all available fields from the serialized data, Fory applies default values to any missing fields.

Usage​

This feature is available when:

  • Native mode is explicitly configured with compatible mode (withXlang(false).withCompatible(true))
  • The target class is detected as a Scala class with default values
  • A field is missing from the serialized data but exists in the target class

No additional configuration is required.

Examples​

Case Class with Default Values​

import org.apache.fory.scala.ForyScala

// Class WITHOUT default values (for serialization)
case class PersonV1(name: String)

// Class WITH default values (for deserialization)
case class PersonV2(name: String, age: Int = 25, city: String = "Unknown")

val fory = ForyScala.builder().withXlang(false)
.withCompatible(true)
.build()

// Serialize using class without default values
val original = PersonV1("John")
val serialized = fory.serialize(original)

// Deserialize into class with default values
// Missing fields will use defaults
val deserialized = fory.deserialize(serialized).asInstanceOf[PersonV2]
// deserialized.name == "John"
// deserialized.age == 25 (default)
// deserialized.city == "Unknown" (default)

Regular Scala Class with Default Values​

// Class WITHOUT default values (for serialization)
class EmployeeV1(val name: String)

// Class WITH default values (for deserialization)
class EmployeeV2(
val name: String,
val age: Int = 30,
val department: String = "Engineering"
)

val fory = ForyScala.builder().withXlang(false)
.withCompatible(true)
.build()

// Serialize using class without default values
val original = new EmployeeV1("Jane")
val serialized = fory.serialize(original)

// Deserialize into class with default values
val deserialized = fory.deserialize(serialized).asInstanceOf[EmployeeV2]
// deserialized.name == "Jane"
// deserialized.age == 30 (default)
// deserialized.department == "Engineering" (default)

Complex Default Values​

Default values can be complex expressions:

// Class WITHOUT default values (for serialization)
case class ConfigV1(name: String)

// Class WITH default values (for deserialization)
case class ConfigV2(
name: String,
settings: Map[String, String] = Map("default" -> "value"),
tags: List[String] = List("default"),
enabled: Boolean = true
)

val fory = ForyScala.builder().withXlang(false)
.withCompatible(true)
.build()

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

val deserialized = fory.deserialize(serialized).asInstanceOf[ConfigV2]
// deserialized.name == "myConfig"
// deserialized.settings == Map("default" -> "value")
// deserialized.tags == List("default")
// deserialized.enabled == true

Nested Case Classes​

object Models {
// Class WITHOUT default values (for serialization)
case class PersonV1(name: String)

// Classes WITH default values (for deserialization)
case class Address(street: String, city: String = "DefaultCity")
case class PersonV2(name: String, address: Address = Address("DefaultStreet"))
}

val fory = ForyScala.builder().withXlang(false)
.withCompatible(true)
.build()

val original = Models.PersonV1("Alice")
val serialized = fory.serialize(original)

val deserialized = fory.deserialize(serialized).asInstanceOf[Models.PersonV2]
// deserialized.name == "Alice"
// deserialized.address == Address("DefaultStreet", "DefaultCity")