我有大约24个案例类,我需要通过在数据存储中更改几个常见元素来进行编程增强,而在数据存储中不支持加入。由于案例类没有针对副本(...)构造函数定义的特征,所以我一直在尝试使用宏 - 作为我看过的基础这篇文章记录了一个宏,并提出了此宏:
当我尝试编译时,我会得到以下内容:
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
方法中实现自定义匿名/增强逻辑,如果需要根据您的实现,可以选择性地使用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