Skip to content

Commit

Permalink
Merge pull request #8 from altoo-ag/adt-enum-serialization-wip
Browse files Browse the repository at this point in the history
Extend Scala 3 enum serialization to handle ADTs
  • Loading branch information
nvollmar authored Nov 28, 2023
2 parents 0c6bcb7 + e6d4100 commit f4d3661
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,16 @@ class ScalaEnumNameSerializer[T <: EnumValue] extends Serializer[T] {
def read(kryo: Kryo, input: Input, typ: Class[? <: T]): T = {
val clazz = kryo.readClass(input).getType
val name = input.readString()
// using value instead of ordinal to make serialization more stable, e.g. allowing reordering without breaking compatibility
clazz.getDeclaredMethod("valueOf", classOf[String]).invoke(null, name).asInstanceOf[T]

try {
// using value instead of ordinal to make serialization more stable, e.g. allowing reordering without breaking compatibility
clazz.getDeclaredMethod("valueOf", classOf[String]).invoke(null, name).asInstanceOf[T]
} catch {
case _: java.lang.NoSuchMethodException =>
// work around Scala 3 ADT-like enums missing valueOf method
val objectClazz = Class.forName(clazz.getName + "$")
objectClazz.getDeclaredField(name).get(null).asInstanceOf[T]
}
}

def write(kryo: Kryo, output: Output, obj: T): Unit = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package io.altoo.serialization.kryo.scala.serializer

import com.esotericsoftware.kryo.util.{DefaultClassResolver, ListReferenceResolver}
import io.altoo.serialization.kryo.scala.ScalaVersionSerializers
import io.altoo.serialization.kryo.scala.serializer.{ScalaEnumNameSerializer, ScalaKryo}
import io.altoo.serialization.kryo.scala.testkit.{AbstractKryoTest, KryoSerializationTesting}
import io.altoo.serialization.kryo.scala.testkit.KryoSerializationTesting
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

Expand All @@ -17,34 +15,52 @@ object ScalaEnumSerializationTest {
case class EmbeddedEnum(sample: Sample) {
def this() = this(null)
}

enum SimpleADT {
case A()
case B
}
}

class ScalaEnumSerializationTest extends AnyFlatSpec with Matchers with KryoSerializationTesting {
class ScalaEnumSerializationTest extends AnyFlatSpec with Matchers with KryoSerializationTesting {
import ScalaEnumSerializationTest.*

val kryo = new ScalaKryo(new DefaultClassResolver(), new ListReferenceResolver())
kryo.setRegistrationRequired(false)
kryo.addDefaultSerializer(classOf[scala.runtime.EnumValue], new ScalaEnumNameSerializer[scala.runtime.EnumValue])


behavior of "Kryo serialization"

it should "reoundtrip enum" in {
it should "round trip enum" in {
kryo.setRegistrationRequired(false)

testSerializationOf(Sample.B)
}

it should "reoundtrip external enum" in {
it should "round trip external enum" in {
kryo.setRegistrationRequired(false)

testSerializationOf(io.altoo.external.ExternalEnum.A)
}

it should "reoundtrip embedded enum" in {
it should "round trip embedded enum" in {
kryo.setRegistrationRequired(false)
kryo.register(classOf[EmbeddedEnum], 46)

testSerializationOf(EmbeddedEnum(Sample.C))
}

it should "round trip adt enum class using generic field serializer" in {
kryo.setRegistrationRequired(false)
kryo.register(classOf[SimpleADT], 47)

testSerializationOf(SimpleADT.A)
}

it should "round trip adt enum object using enum serializer" in {
kryo.setRegistrationRequired(false)
kryo.register(classOf[SimpleADT], 47)

testSerializationOf(SimpleADT.B)
}
}

0 comments on commit f4d3661

Please sign in to comment.