使用Shapeless将嵌套的case类转换为嵌套的Maps

时间:2015-07-26 04:14:04

标签: scala shapeless

我正在尝试使用Shapeless来解决this问题,总结一下,将嵌套案例类转换为Map [String,Any],这里是示例:

let randomEight = arc4random_uniform(5)+1

override func viewDidLoad() {
    super.viewDidLoad()

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


 if randomEight == 1 {
    randomLabel.textColor = UIColor.cyanColor()
    }
    else if randomEight == 2 {

        randomLabel.textColor = UIColor.purpleColor()
    }

    else if randomEight == 3{
        randomLabel.textColor = UIColor.orangeColor()

    } else if randomEight == 4 {

        randomLabel.textColor = UIColor.blueColor()
    }
    else {

        randomLabel.textColor = UIColor.greenColor()
    }
    return randomLabel

}

它希望将case class Person(name:String, address:Address) case class Address(street:String, zip:Int) val p = Person("Tom", Address("Jefferson st", 10000)) 转换为以下内容:

p

我正在尝试使用Shapeless Map("name" -> "Tom", "address" -> Map("street" -> "Jefferson st", "zip" -> 10000)) ,这是我迄今为止所做的:

LabelledGeneric

我正在尝试使用递归编写器来检查每个值,并尝试为值中的每个元素创建Map。上面的代码工作正常但是当我想要使用以下代码迭代import shapeless._ import record._, syntax.singleton._ import ops.record._ import shapeless.ops.record._ def writer[T,A<:HList,H<:HList](t:T) (implicit lGeneric:LabelledGeneric.Aux[T,A], kys:Keys.Aux[A,H], vls:Values[A]) = { val tGen = lGeneric.to(t) val keys = Keys[lGeneric.Repr].apply val values = Values[lGeneric.Repr].apply(tGen) println(keys) println(values) } 时,我得到了这些错误。

values

我不知道为什么我会收到这个错误。我也很高兴知道使用Shapeless是否有更简单的方法来完成整个事情。

2 个答案:

答案 0 :(得分:24)

如果您想在flatMap上对其类型不是静态知道的HList执行操作,则需要提供证据(以隐式参数的形式)操作实际上可用于该类型。这就是编译器抱怨丢失FlatMapper个实例的原因 - 如果没有它们,它就不知道如何flatMap(identity)超过任意HList

完成此类操作的更简洁方法是定义自定义类型类。 Shapeless已经为记录提供了一个ToMap类型类,我们可以把它作为一个起点,虽然它没有提供你正在寻找的东西(它在嵌套的case类上不能递归地工作)。

我们可以写下面的内容:

import shapeless._, labelled.FieldType, record._

trait ToMapRec[L <: HList] { def apply(l: L): Map[String, Any] }

现在我们需要为三种情况提供实例。第一种情况是基本情况 - 空记录 - 它由下面的hnilToMapRec处理。

第二种情况是我们知道如何转换记录的尾部,我们知道头部是我们也可以递归转换的(hconsToMapRec0这里)。

最后一种情况类似,但对于没有ToMapRec个实例(hconsToMapRec1)的负责人而言。请注意,我们需要使用LowPriority特征来确保此实例在hconsToMapRec0方面的优先级正确 - 如果我们不这样做,两者将具有相同的优先级并且我们会收到错误关于模棱两可的实例。

trait LowPriorityToMapRec {
  implicit def hconsToMapRec1[K <: Symbol, V, T <: HList](implicit
    wit: Witness.Aux[K],
    tmrT: ToMapRec[T]
  ): ToMapRec[FieldType[K, V] :: T] = new ToMapRec[FieldType[K, V] :: T] {
    def apply(l: FieldType[K, V] :: T): Map[String, Any] =
      tmrT(l.tail) + (wit.value.name -> l.head)
  }
}

object ToMapRec extends LowPriorityToMapRec {
  implicit val hnilToMapRec: ToMapRec[HNil] = new ToMapRec[HNil] {
    def apply(l: HNil): Map[String, Any] = Map.empty
  }

  implicit def hconsToMapRec0[K <: Symbol, V, R <: HList, T <: HList](implicit
    wit: Witness.Aux[K],
    gen: LabelledGeneric.Aux[V, R],
    tmrH: ToMapRec[R],
    tmrT: ToMapRec[T]
  ): ToMapRec[FieldType[K, V] :: T] = new ToMapRec[FieldType[K, V] :: T] {
    def apply(l: FieldType[K, V] :: T): Map[String, Any] =
      tmrT(l.tail) + (wit.value.name -> tmrH(gen.to(l.head)))
  }
}

最后,为方便起见,我们提供了一些语法:

implicit class ToMapRecOps[A](val a: A) extends AnyVal {
  def toMapRec[L <: HList](implicit
    gen: LabelledGeneric.Aux[A, L],
    tmr: ToMapRec[L]
  ): Map[String, Any] = tmr(gen.to(a))
}

然后我们可以证明它有效:

scala> p.toMapRec
res0: Map[String,Any] = Map(address -> Map(zip -> 10000, street -> Jefferson st), name -> Tom)

请注意,这对于嵌套案例类在列表,元组等中的类型不起作用,但您可以非常直接地将它扩展到这些情况。

答案 1 :(得分:3)

我对Travis Brown提供的方法有疑问 某些嵌套案例类未转换为Map https://scalafiddle.io/sf/cia2jTa/0

答案被发现here 要纠正解决方案,只需将隐式参数中的ToMapRec [T]包装到Lazy [ToMapRec [T]]。更正了小提琴https://scalafiddle.io/sf/cia2jTa/1