加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 嵌套案例类之间的递归转换,其中目标中的字段是源类的未

发布时间:2020-12-16 09:56:10 所属栏目:安全 来源:网络整理
导读:给定一对具有嵌套case类的case类,Source和Target,并且在每个嵌套级别,Target中的字段都是Source中的字段的未对齐子集,有没有办法编写一个通用的Shapeless-powered变换来自目标来源? 例如,给定以下内部和外部类: object Internal { case class User( firstN
给定一对具有嵌套case类的case类,Source和Target,并且在每个嵌套级别,Target中的字段都是Source中的字段的未对齐子集,有没有办法编写一个通用的Shapeless-powered变换来自目标来源?

例如,给定以下内部和外部类:

object Internal {
  case class User(
    firstName: String,lastName: String,isAdmin: Boolean,address: Address
  )

  case class Address(
    street: String,country: String,blacklisted: Boolean
  )
}

object External {
  // Note that isAdmin is missing and the fields are jumbled
  case class User(
    lastName: String,firstName: String,address: Address
  )

  // blacklisted is gone
  case class Address(
    street: String,country: String
  )
}

我希望能够做类似的事情

val internalUser = Internal.User(
  firstName = "Joe",lastName = "Blow",isAdmin = false,address = Internal.Address(
    street = "Sesame",country = "U-S-A",blacklisted = false
  )
)

val externalUser = Transform.into[External.User](internalUser)

我有some code负责选择子集并对齐字段,但递归部分更具挑战性:

import shapeless._,ops.hlist.Align,ops.hlist.SelectAll,SelectAll._

class Transform[T] {

  // The fun stuff. Given an S,returns a T,if S has the right (subset of) fields
  def apply[S,SR <: HList,TR <: HList](s: S)(
      implicit
      genS: LabelledGeneric.Aux[S,SR],genT: LabelledGeneric.Aux[T,TR],selectAll: SelectAll[SR,align: Align[SelectAll[SR,TR]#Out,TR]): T =
    genT.from(align(selectAll(genS.to(s))))
}

object Transform {

  // Convenience method for building an instance of `Transform`
  def into[T] = new Transform[T]
}

我已经看了this SO question,但那里的答案并没有考虑到这些字段是另一个字符集的未对齐子集的事实.

解决方法

这是一个有趣的练习,将各种原始图形拼凑在一起以获得结果.以下已经使用Scala 2.12.6和2.13.0-M5进行了无形2.3.3的测试…

我们可以像这样定义一个Transform类型,

import shapeless._,ops.hlist.ZipWithKeys,ops.record.{ Keys,SelectAll,Values }

trait Transform[T,U] {
  def apply(t: T): U
}

object Transform {
  def into[U] = new MkTransform[U]
  class MkTransform[U] {
    def apply[T](t: T)(implicit tt: Transform[T,U]): U = tt(t)
  }

  // The identity transform
  implicit def transformId[T]: Transform[T,T] =
    new Transform[T,T] {
      def apply(t: T): T = t
    }

  // Transform for HLists
  implicit def transformHCons[H1,T1 <: HList,H2,T2 <: HList]
    (implicit
      th: Transform[H1,H2],tt: Transform[T1,T2]
    ): Transform[H1 :: T1,H2 :: T2] =
    new Transform[H1 :: T1,H2 :: T2] {
      def apply(r: H1 :: T1): H2 :: T2 = th(r.head) :: tt(r.tail)
    }

  // Transform for types which have a LabelledGeneric representation as
  // a shapeless record
  implicit def transformGen
    [T,U,TR <: HList,UR <: HList,UK <: HList,UV <: HList,TS <: HList]
    (implicit
      genT:    LabelledGeneric.Aux[T,// T <-> corresponding record
      genU:    LabelledGeneric.Aux[U,UR],// U <-> corresponding record
      keysU:   Keys.Aux[UR,UK],// Keys of the record for U
      valuesU: Values.Aux[UR,UV],// Values of the record for U
      selT:    SelectAll.Aux[TR,UK,TS],// Select the values of the record of T
                                            //   corresponding to the keys of U
      trans:   Lazy[Transform[TS,UV]],// Transform the selected values
      zipKeys: ZipWithKeys.Aux[UK,UV,// Construct a new record of U from the
                                            //   transformed values
    ): Transform[T,U] =
    new Transform[T,U] {
      def apply(t: T): U = {
        genU.from(zipKeys(trans.value(selT(genT.to(t)))))
      }
    }
}

有趣的案例是transformGen.类型变量T和U是源类型和目标类型,并且在呼叫站点处是固定的.其余的类型变量按顺序从左到右求解,因为隐式参数从上到下被解析…在大多数情况下,给定前面的类型参数解析每个隐式的最终类型参数,并且解决方案流向右/直到后来的决议.

还要注意使用无形的Lazy守护递归隐式参数trans.对于您的示例,这不是必需的,但可能是更复杂或递归的情况.另请注意,在Scala 2.13.0-M5及更高版本中,trans可以定义为名称隐式参数.

现在,根据你的定义,

val internalUser = Internal.User(
  firstName = "Joe",blacklisted = false
  )
)

以下按预期工作,

val expectedExternalUser = External.User(
  lastName = "Blow",firstName = "Joe",address = External.Address(
    street = "Sesame",)
)

val externalUser = Transform.into[External.User](internalUser)

assert(externalUser == expectedExternalUser)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读