如何使用 shapeless 将泛型 Map[String, Any] 转换为泛型函数内的 case class?
How to use shapeless to convert generic Map[String, Any] to case class inside generic function?
我正在尝试按照此处的答案进行操作
也就是说,我希望能够将(可能嵌套的)Map[String, Any]
转换为大小写 class。
scalaVersion := "2.11.8"
val shapelessV = "2.3.3"
如果我尝试将上述答案中的代码包装在另一个通用函数中,我似乎无法编译它
import shapeless._, labelled._
import FromMap._
def usesGenerics[P](map: Map[String, Any]): P = {
to[P].from(mp).get
}
我得到以下编译错误
could not find implicit value for parameter gen: shapeless.LabelledGeneric.Aux[P,R]
UDPATE
这种方法更简单和完整(处理更多边缘情况):
完整答案
这结合了 Travis 的最初想法和 Dmytros 修复,最后进行了一些简化
import shapeless._, labelled.{FieldType, field}
trait FromMap[L <: HList] {
def apply(m: Map[String, Any]): Option[L]
}
trait LowPriorityFromMap {
implicit def hconsFromMap1[K <: Symbol, V, T <: HList](implicit
witness: Witness.Aux[K],
typeable: Typeable[V],
fromMapT: Lazy[FromMap[T]]
): FromMap[FieldType[K, V] :: T] = new FromMap[FieldType[K, V] :: T] {
def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {
v <- m.get(witness.value.name)
h <- typeable.cast(v)
t <- fromMapT.value(m)
} yield field[K](h) :: t
}
}
object FromMap extends LowPriorityFromMap {
implicit val hnilFromMap: FromMap[HNil] = new FromMap[HNil] {
def apply(m: Map[String, Any]): Option[HNil] = Some(HNil)
}
implicit def hconsFromMap0[K <: Symbol, V, R <: HList, T <: HList](implicit
witness: Witness.Aux[K],
gen: LabelledGeneric.Aux[V, R],
fromMapH: FromMap[R],
fromMapT: FromMap[T]
): FromMap[FieldType[K, V] :: T] =
new FromMap[FieldType[K, V] :: T] {
def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {
v <- m.get(witness.value.name)
r <- Typeable[Map[String, Any]].cast(v)
h <- fromMapH(r)
t <- fromMapT(m)
} yield field[K](gen.from(h)) :: t
}
}
trait CaseClassFromMap[P <: Product] {
def apply(m: Map[String, Any]): Option[P]
}
object CaseClassFromMap {
implicit def mk[P <: Product, R <: HList](implicit gen: LabelledGeneric.Aux[P, R],
fromMap: FromMap[R]): CaseClassFromMap[P] = new CaseClassFromMap[P] {
def apply(m: Map[String, Any]): Option[P] = fromMap(m).map(gen.from)
}
def apply[P <: Product](map: Map[String, Any])(implicit fromMap: CaseClassFromMap[P]): P = fromMap(map).get
}
这对你有用吗?
def usesGenerics[P, R <: HList](map: Map[String, Any])(implicit gen: LabelledGeneric.Aux[P, R], fromMap: FromMap[R]): P = {
to[P].from[R](map).get
}
你还需要一种class
trait CaseClassFromMap[P <: Product] {
def apply(m: Map[String, Any]): Option[P]
}
object CaseClassFromMap {
implicit def mk[P <: Product, R <: HList](implicit
gen: LabelledGeneric.Aux[P, R],
fromMap: FromMap[R]
): CaseClassFromMap[P] = new CaseClassFromMap[P] {
def apply(m: Map[String, Any]): Option[P] = to[P].from[R](m)
}
}
def usesGenerics[P <: Product](map: Map[String, Any])(implicit fromMap: CaseClassFromMap[P]): P = {
fromMap(map).get
}
usesGenerics[Person](mp)
我正在尝试按照此处的答案进行操作
也就是说,我希望能够将(可能嵌套的)Map[String, Any]
转换为大小写 class。
scalaVersion := "2.11.8"
val shapelessV = "2.3.3"
如果我尝试将上述答案中的代码包装在另一个通用函数中,我似乎无法编译它
import shapeless._, labelled._
import FromMap._
def usesGenerics[P](map: Map[String, Any]): P = {
to[P].from(mp).get
}
我得到以下编译错误
could not find implicit value for parameter gen: shapeless.LabelledGeneric.Aux[P,R]
UDPATE
这种方法更简单和完整(处理更多边缘情况):
完整答案
这结合了 Travis 的最初想法和 Dmytros 修复,最后进行了一些简化
import shapeless._, labelled.{FieldType, field}
trait FromMap[L <: HList] {
def apply(m: Map[String, Any]): Option[L]
}
trait LowPriorityFromMap {
implicit def hconsFromMap1[K <: Symbol, V, T <: HList](implicit
witness: Witness.Aux[K],
typeable: Typeable[V],
fromMapT: Lazy[FromMap[T]]
): FromMap[FieldType[K, V] :: T] = new FromMap[FieldType[K, V] :: T] {
def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {
v <- m.get(witness.value.name)
h <- typeable.cast(v)
t <- fromMapT.value(m)
} yield field[K](h) :: t
}
}
object FromMap extends LowPriorityFromMap {
implicit val hnilFromMap: FromMap[HNil] = new FromMap[HNil] {
def apply(m: Map[String, Any]): Option[HNil] = Some(HNil)
}
implicit def hconsFromMap0[K <: Symbol, V, R <: HList, T <: HList](implicit
witness: Witness.Aux[K],
gen: LabelledGeneric.Aux[V, R],
fromMapH: FromMap[R],
fromMapT: FromMap[T]
): FromMap[FieldType[K, V] :: T] =
new FromMap[FieldType[K, V] :: T] {
def apply(m: Map[String, Any]): Option[FieldType[K, V] :: T] = for {
v <- m.get(witness.value.name)
r <- Typeable[Map[String, Any]].cast(v)
h <- fromMapH(r)
t <- fromMapT(m)
} yield field[K](gen.from(h)) :: t
}
}
trait CaseClassFromMap[P <: Product] {
def apply(m: Map[String, Any]): Option[P]
}
object CaseClassFromMap {
implicit def mk[P <: Product, R <: HList](implicit gen: LabelledGeneric.Aux[P, R],
fromMap: FromMap[R]): CaseClassFromMap[P] = new CaseClassFromMap[P] {
def apply(m: Map[String, Any]): Option[P] = fromMap(m).map(gen.from)
}
def apply[P <: Product](map: Map[String, Any])(implicit fromMap: CaseClassFromMap[P]): P = fromMap(map).get
}
这对你有用吗?
def usesGenerics[P, R <: HList](map: Map[String, Any])(implicit gen: LabelledGeneric.Aux[P, R], fromMap: FromMap[R]): P = {
to[P].from[R](map).get
}
你还需要一种class
trait CaseClassFromMap[P <: Product] {
def apply(m: Map[String, Any]): Option[P]
}
object CaseClassFromMap {
implicit def mk[P <: Product, R <: HList](implicit
gen: LabelledGeneric.Aux[P, R],
fromMap: FromMap[R]
): CaseClassFromMap[P] = new CaseClassFromMap[P] {
def apply(m: Map[String, Any]): Option[P] = to[P].from[R](m)
}
}
def usesGenerics[P <: Product](map: Map[String, Any])(implicit fromMap: CaseClassFromMap[P]): P = {
fromMap(map).get
}
usesGenerics[Person](mp)