为什么CASE类副本的Scala宏失败



我有大约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

最新更新