我正在尝试使用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是否有更简单的方法来完成整个事情。
答案 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