title: Scala Serialization Guide sidebar_position: 3 id: scala_serialization license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Apache Fory™ supports all scala object serialization:
case class serialization supportedpojo/bean class serialization supportedobject singleton serialization supportedcollection serialization supportedtuple/either and basic types are all supported too.Scala 2 and 3 are both supported.
To add a dependency on Apache Fory™ scala for with sbt, use the following:
libraryDependencies += "org.apache.fory" %% "fory-scala" % "0.13.1"
case class Person(name: String, id: Long, github: String) case class Point(x : Int, y : Int, z : Int) object ScalaExample { val fory: Fory = Fory.builder().withScalaOptimizationEnabled(true).build() // Register optimized fory serializers for scala ScalaSerializers.registerSerializers(fory) fory.register(classOf[Person]) fory.register(classOf[Point]) def main(args: Array[String]): Unit = { val p = Person("Shawn Yang", 1, "https://github.com/chaokunyang") println(fory.deserialize(fory.serialize(p))) println(fory.deserialize(fory.serialize(Point(1, 2, 3)))) } }
When using fory for scala serialization, you should create fory at least with following options:
import org.apache.fory.Fory import org.apache.fory.serializer.scala.ScalaSerializers val fory = Fory.builder().withScalaOptimizationEnabled(true).build() // Register optimized fory serializers for scala ScalaSerializers.registerSerializers(fory)
Depending on the object types you serialize, you may need to register some scala internal types:
fory.register(Class.forName("scala.Enumeration.Val"))
If you want to avoid such registration, you can disable class registration by ForyBuilder#requireClassRegistration(false). Note that this option allow to deserialize objects unknown types, more flexible but may be insecure if the classes contains malicious code.
And circular references are common in scala, Reference tracking should be enabled by ForyBuilder#withRefTracking(true). If you don't enable reference tracking, StackOverflowError may happen for some scala versions when serializing scala Enumeration.
Note that fory instance should be shared between multiple serialization, the creation of fory instance is not cheap.
If you use shared fory instance across multiple threads, you should create ThreadSafeFory instead by ForyBuilder#buildThreadSafeFory() instead.
case class Person(github: String, age: Int, id: Long) val p = Person("https://github.com/chaokunyang", 18, 1) println(fory.deserialize(fory.serialize(p))) println(fory.deserializeJavaObject(fory.serializeJavaObject(p)))
class Foo(f1: Int, f2: String) { override def toString: String = s"Foo($f1, $f2)" } println(fory.deserialize(fory.serialize(Foo(1, "chaokunyang"))))
object singleton { } val o1 = fory.deserialize(fory.serialize(singleton)) val o2 = fory.deserialize(fory.serialize(singleton)) println(o1 == o2)
val seq = Seq(1,2) val list = List("a", "b") val map = Map("a" -> 1, "b" -> 2) println(fory.deserialize(fory.serialize(seq))) println(fory.deserialize(fory.serialize(list))) println(fory.deserialize(fory.serialize(map)))
val tuple = (100, 10000L) //Tuple2 println(fory.deserialize(fory.serialize(tuple))) val tuple = (100, 10000L, 10000L, "str") //Tuple4 println(fory.deserialize(fory.serialize(tuple)))
enum Color { case Red, Green, Blue } println(fory.deserialize(fory.serialize(Color.Green)))
object ColorEnum extends Enumeration { type ColorEnum = Value val Red, Green, Blue = Value } println(fory.deserialize(fory.serialize(ColorEnum.Green)))
val opt: Option[Long] = Some(100) println(fory.deserialize(fory.serialize(opt))) val opt1: Option[Long] = None println(fory.deserialize(fory.serialize(opt1)))
Fory supports Scala class default values during deserialization when using compatible mode. This feature enables forward/backward compatibility when case classes or regular Scala classes have default parameters.
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.
Fory supports default values for:
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).
Default Value Discovery:
apply$default$1, apply$default$2, etc.$default$1, $default$2, etc.Field Mapping: During deserialization, Fory identifies fields that exist in the target class but are missing from the serialized data.
Value Application: After reading all available fields from the serialized data, Fory applies default values to any missing fields using direct field access for optimal performance.
This feature is automatically enabled when:
withCompatibleMode(CompatibleMode.COMPATIBLE))No additional configuration is required.
// Class WITHOUT default values (for serialization) case class PersonNoDefaults(name: String) // Class WITH default values (for deserialization) case class PersonWithDefaults(name: String, age: Int = 25, city: String = "Unknown") val fory = Fory.builder() .withCompatibleMode(CompatibleMode.COMPATIBLE) .withScalaOptimizationEnabled(true) .build() // Serialize using class without default values val original = PersonNoDefaults("John") val serialized = fory.serialize(original) // Deserialize into class with default values - missing fields will use defaults val deserialized = fory.deserialize(serialized, classOf[PersonWithDefaults]) // deserialized.name will be "John" // deserialized.age will be 25 (default) // deserialized.city will be "Unknown" (default)
// Class WITHOUT default values (for serialization) class EmployeeNoDefaults(val name: String) // Class WITH default values (for deserialization) class EmployeeWithDefaults(val name: String, val age: Int = 30, val department: String = "Engineering") val fory = Fory.builder() .withCompatibleMode(CompatibleMode.COMPATIBLE) .withScalaOptimizationEnabled(true) .build() // Serialize using class without default values val original = new EmployeeNoDefaults("Jane") val serialized = fory.serialize(original) // Deserialize into class with default values - missing fields will use defaults val deserialized = fory.deserialize(serialized, classOf[EmployeeWithDefaults]) // deserialized.name will be "Jane" // deserialized.age will be 30 (default) // deserialized.department will be "Engineering" (default)
// Class WITHOUT default values (for serialization) case class ConfigurationNoDefaults(name: String) // Class WITH default values (for deserialization) case class ConfigurationWithDefaults( name: String, settings: Map[String, String] = Map("default" -> "value"), tags: List[String] = List("default"), enabled: Boolean = true ) val fory = Fory.builder() .withCompatibleMode(CompatibleMode.COMPATIBLE) .withScalaOptimizationEnabled(true) .build() // Serialize using class without default values val original = ConfigurationNoDefaults("myConfig") val serialized = fory.serialize(original) // Deserialize into class with default values - missing fields will use defaults val deserialized = fory.deserialize(serialized, classOf[ConfigurationWithDefaults]) // deserialized.name will be "myConfig" // deserialized.settings will be Map("default" -> "value") // deserialized.tags will be List("default") // deserialized.enabled will be true
object NestedClasses { // Class WITHOUT default values (for serialization) case class SimplePerson(name: String) // Class WITH default values (for deserialization) case class Address(street: String, city: String = "DefaultCity") case class PersonWithDefaults(name: String, address: Address = Address("DefaultStreet")) } val fory = Fory.builder() .withCompatibleMode(CompatibleMode.COMPATIBLE) .withScalaOptimizationEnabled(true) .build() // Serialize using class without default values val original = NestedClasses.SimplePerson("Alice") val serialized = fory.serialize(original) // Deserialize into class with default values - missing address field will use default val deserialized = fory.deserialize(serialized, classOf[NestedClasses.PersonWithDefaults]) // deserialized.name will be "Alice" // deserialized.address will be Address("DefaultStreet", "DefaultCity")