为什么 Scala 宏 for case class 复制失败?
Why does the Scala Macro for case class copy fail?
我有大约 24 个案例 类,我需要通过在不支持连接的数据存储中序列化之前更改几个常见元素来以编程方式增强它们。由于 case 类 没有为 copy(...) 构造函数定义的特征,我一直在尝试使用宏 - 作为我看过的基础
this post documenting a macro 想出这个宏:
当我尝试编译时,我得到以下信息:
import java.util.UUID
import org.joda.time.DateTime
import scala.language.experimental.macros
trait RecordIdentification {
val receiverId: Option[String]
val transmitterId: Option[String]
val patientId: Option[UUID]
val streamType: Option[String]
val sequenceNumber: Option[Long]
val postId: Option[UUID]
val postedDateTime: Option[DateTime]
}
object WithRecordIdentification {
import scala.reflect.macros.Context
def withId[T, I](entity: T, id: I): T = macro withIdImpl[T, I]
def withIdImpl[T: c.WeakTypeTag, I: c.WeakTypeTag](c: Context)(
entity: c.Expr[T], id: c.Expr[I]
): c.Expr[T] = {
import c.universe._
val tree = reify(entity.splice).tree
val copy = entity.actualType.member(newTermName("copy"))
val params = copy match {
case s: MethodSymbol if (s.paramss.nonEmpty) => s.paramss.head
case _ => c.abort(c.enclosingPosition, "No eligible copy method!")
}
c.Expr[T](Apply(
Select(tree, copy),
AssignOrNamedArg(Ident("postId"), reify(id.splice).tree) ::
AssignOrNamedArg(Ident("patientId"), reify(id.splice).tree) ::
AssignOrNamedArg(Ident("receiverId"), reify(id.splice).tree) ::
AssignOrNamedArg(Ident("transmitterId"), reify(id.splice).tree) ::
AssignOrNamedArg(Ident("sequenceNumber"), reify(id.splice).tree) :: Nil
))
}
}
我用类似的东西调用它:
class GenericAnonymizer[A <: RecordIdentification]() extends Schema {
def anonymize(dataPost: A, header: DaoDataPostHeader): A = WithRecordIdentification.withId(dataPost, header)
}
但是我得到一个编译错误:
Error:(44, 71) type mismatch;
found : com.dexcom.rt.model.DaoDataPostHeader
required: Option[String]
val copied = WithRecordIdentification.withId(sampleGlucoseRecord, header)
Error:(44, 71) type mismatch;
found : com.dexcom.rt.model.DaoDataPostHeader
required: Option[java.util.UUID]
val copied = WithRecordIdentification.withId(sampleGlucoseRecord, header)
Error:(44, 71) type mismatch;
found : com.dexcom.rt.model.DaoDataPostHeader
required: Option[Long]
val copied = WithRecordIdentification.withId(sampleGlucoseRecord, header)
我不太确定如何更改宏以支持多个参数...有什么明智的建议吗?
假设您有一组以下情况 类,您希望在序列化之前对某些属性进行匿名处理。
case class MyRecordA(var receiverId: String, var y: Int)
case class MyRecordB(var transmitterId: Int, var y: Int)
case class MyRecordC(var patientId: UUID, var y: Int)
case class MyRecordD(var streamType: String, var y: Int)
case class MyRecordE(var sequenceNumber: String, var streamType: String, var y: Int)
您可以使用 Scala 反射库在运行时改变实例的属性。您可以在 implicit
anonymize
方法中实现您的自定义 anonymize/enhancing 逻辑,如果需要,Mutator
可以根据您的实现有选择地更改给定实例的字段。
import java.util.UUID
import scala.reflect.runtime.{universe => ru}
implicit def anonymize(field: String /* field name */, value: Any /* use current field value if reqd */): Option[Any] = field match {
case "receiverId" => Option(value.toString.hashCode)
case "transmitterId" => Option(22)
case "patientId" => Option(UUID.randomUUID())
case _ => None
}
implicit class Mutator[T: ru.TypeTag](i: T)(implicit c: scala.reflect.ClassTag[T], anonymize: (String, Any) => Option[Any]) {
def mask = {
val m = ru.runtimeMirror(i.getClass.getClassLoader)
ru.typeOf[T].members.filter(!_.isMethod).foreach(s => {
val fVal = m.reflect(i).reflectField(s.asTerm)
anonymize(s.name.decoded.trim, fVal.get).foreach(fVal.set)
})
i
}
}
现在您可以在任何实例上调用屏蔽:
val maskedRecord = MyRecordC(UUID.randomUUID(), 2).mask
我有大约 24 个案例 类,我需要通过在不支持连接的数据存储中序列化之前更改几个常见元素来以编程方式增强它们。由于 case 类 没有为 copy(...) 构造函数定义的特征,我一直在尝试使用宏 - 作为我看过的基础 this post documenting a macro 想出这个宏:
当我尝试编译时,我得到以下信息:
import java.util.UUID
import org.joda.time.DateTime
import scala.language.experimental.macros
trait RecordIdentification {
val receiverId: Option[String]
val transmitterId: Option[String]
val patientId: Option[UUID]
val streamType: Option[String]
val sequenceNumber: Option[Long]
val postId: Option[UUID]
val postedDateTime: Option[DateTime]
}
object WithRecordIdentification {
import scala.reflect.macros.Context
def withId[T, I](entity: T, id: I): T = macro withIdImpl[T, I]
def withIdImpl[T: c.WeakTypeTag, I: c.WeakTypeTag](c: Context)(
entity: c.Expr[T], id: c.Expr[I]
): c.Expr[T] = {
import c.universe._
val tree = reify(entity.splice).tree
val copy = entity.actualType.member(newTermName("copy"))
val params = copy match {
case s: MethodSymbol if (s.paramss.nonEmpty) => s.paramss.head
case _ => c.abort(c.enclosingPosition, "No eligible copy method!")
}
c.Expr[T](Apply(
Select(tree, copy),
AssignOrNamedArg(Ident("postId"), reify(id.splice).tree) ::
AssignOrNamedArg(Ident("patientId"), reify(id.splice).tree) ::
AssignOrNamedArg(Ident("receiverId"), reify(id.splice).tree) ::
AssignOrNamedArg(Ident("transmitterId"), reify(id.splice).tree) ::
AssignOrNamedArg(Ident("sequenceNumber"), reify(id.splice).tree) :: Nil
))
}
}
我用类似的东西调用它:
class GenericAnonymizer[A <: RecordIdentification]() extends Schema {
def anonymize(dataPost: A, header: DaoDataPostHeader): A = WithRecordIdentification.withId(dataPost, header)
}
但是我得到一个编译错误:
Error:(44, 71) type mismatch;
found : com.dexcom.rt.model.DaoDataPostHeader
required: Option[String]
val copied = WithRecordIdentification.withId(sampleGlucoseRecord, header)
Error:(44, 71) type mismatch;
found : com.dexcom.rt.model.DaoDataPostHeader
required: Option[java.util.UUID]
val copied = WithRecordIdentification.withId(sampleGlucoseRecord, header)
Error:(44, 71) type mismatch;
found : com.dexcom.rt.model.DaoDataPostHeader
required: Option[Long]
val copied = WithRecordIdentification.withId(sampleGlucoseRecord, header)
我不太确定如何更改宏以支持多个参数...有什么明智的建议吗?
假设您有一组以下情况 类,您希望在序列化之前对某些属性进行匿名处理。
case class MyRecordA(var receiverId: String, var y: Int)
case class MyRecordB(var transmitterId: Int, var y: Int)
case class MyRecordC(var patientId: UUID, var y: Int)
case class MyRecordD(var streamType: String, var y: Int)
case class MyRecordE(var sequenceNumber: String, var streamType: String, var y: Int)
您可以使用 Scala 反射库在运行时改变实例的属性。您可以在 implicit
anonymize
方法中实现您的自定义 anonymize/enhancing 逻辑,如果需要,Mutator
可以根据您的实现有选择地更改给定实例的字段。
import java.util.UUID
import scala.reflect.runtime.{universe => ru}
implicit def anonymize(field: String /* field name */, value: Any /* use current field value if reqd */): Option[Any] = field match {
case "receiverId" => Option(value.toString.hashCode)
case "transmitterId" => Option(22)
case "patientId" => Option(UUID.randomUUID())
case _ => None
}
implicit class Mutator[T: ru.TypeTag](i: T)(implicit c: scala.reflect.ClassTag[T], anonymize: (String, Any) => Option[Any]) {
def mask = {
val m = ru.runtimeMirror(i.getClass.getClassLoader)
ru.typeOf[T].members.filter(!_.isMethod).foreach(s => {
val fVal = m.reflect(i).reflectField(s.asTerm)
anonymize(s.name.decoded.trim, fVal.get).foreach(fVal.set)
})
i
}
}
现在您可以在任何实例上调用屏蔽:
val maskedRecord = MyRecordC(UUID.randomUUID(), 2).mask