我定义这个是为了序列化任何字段为标量的案例类:
import scala.compiletime.*
import scala.deriving.*
trait Scalar[T]:
def convert(t: T): AnyRef
val sqlType: String
given Scalar[String] with
def convert(t: String) = t
val sqlType = "VARCHAR"
given Scalar[Int] with
def convert(t: Int) = Integer.valueOf(t)
val sqlType = "INTEGER"
trait Serializable[T]:
def serialize(v: T): Map[String, AnyRef]
def fields(v: T): Map[String, String]
object Serializable:
inline def summonAll[T <: Tuple]: List[Scalar[?]] =
inline erasedValue[T] match
case _: EmptyTuple => Nil
case _: (t *: ts) => summonInline[Scalar[t]] :: summonAll[ts]
inline given derived[A <: Product](using m: Mirror.ProductOf[A]): Serializable[A] =
val scalars = summonAll[m.MirroredElemTypes]
new Serializable[A]:
override def serialize(v: A): Map[String, AnyRef] =
val pro = v.asInstanceOf[Product]
pro.productElementNames.zip(pro.productIterator).zip(scalars).map {
case ((name, value), scalar) =>
name -> scalar.asInstanceOf[Scalar[Any]].convert(value)
}.toMap
override def fields(v: A): Map[String, String] =
val pro = v.asInstanceOf[Product]
pro.productElementNames.zip(scalars).map {
case (name, scalar) =>
name -> scalar.asInstanceOf[Scalar[Any]].sqlType
}.toMap
case class Person(name: String, age: Int) derives Serializable
它的工作原理要归功于神奇的编译时镜像。
但是我需要定义一下:
trait Serializable[T]:
def serialize(v: T): Map[String, AnyRef]
val fields: Map[String, String]
而不是:
def fields(v: T): Map[String, String]
因为字段不依赖于实例,而是依赖于类类型。
问题是,当我没有案例类的实例时,我不知道如何获取字段名称。
这是我找到的解决方案,也许它不是最好的,但它有效:
#!/usr/bin/env -S scala-cli -j system
import scala.compiletime.*
import scala.deriving.*
trait Scalar[T]:
def convert(t: T): AnyRef
val sqlType: String
given Scalar[String] with
def convert(t: String) = t
val sqlType = "VARCHAR"
given Scalar[Int] with
def convert(t: Int) = Integer.valueOf(t)
val sqlType = "INTEGER"
trait Serializable[T]:
def serialize(v: T): Map[String, AnyRef]
val fields: Map[String, String]
object Serializable:
inline def summonAll[T <: Tuple]: List[Scalar[?]] =
inline erasedValue[T] match
case _: EmptyTuple => Nil
case _: (t *: ts) => summonInline[Scalar[t]] :: summonAll[ts]
inline given derived[A <: Product](using m: Mirror.ProductOf[A]): Serializable[A] =
val scalars = summonAll[m.MirroredElemTypes]
new Serializable[A]:
override def serialize(v: A): Map[String, AnyRef] =
val pro = v.asInstanceOf[Product]
pro.productElementNames.zip(pro.productIterator).zip(scalars).map {
case ((name, value), scalar) =>
name -> scalar.asInstanceOf[Scalar[Any]].convert(value)
}.toMap
override val fields: Map[String, String] =
import scala.compiletime.constValueTuple
val nombres = constValueTuple[m.MirroredElemLabels].productIterator
nombres.zip(scalars).map {
case (name, scalar) =>
name.toString() -> scalar.asInstanceOf[Scalar[Any]].sqlType
}.toMap
case class Person(name: String, age: Int) derives Serializable
val sp = summon[Serializable[Person]]
assert(sp.fields("age") == "INTEGER")