使用 Shapeless 将 Map[String,Any] 转换为 case class
Converting Map[String,Any] to a case class using Shapeless
问题 询问有关将案例 class 映射到 Map[String,Any] 的问题。我想知道反过来会怎样,将 Map[String,Any] 转换为 case class。给定以下地图:
val mp = Map("name" -> "Tom", "address" -> Map("street" -> "Jefferson st", "zip" -> 10000))
将其转换为 Person
的 class 大小写:
case class Person(name:String, address:Address)
case class Address(street:String, zip:Int)
val p = Person("Tom", Address("Jefferson st", 10000))
像这样:
val newP = mp.asCC[Person]
assert(newP.get == p)
我应该如何使用 Shapeless。
这是一个现成的、大部分未经测试的解决方案。首先是类型 class:
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
}
}
为了方便起见,还有一个助手class:
class ConvertHelper[A] {
def from[R <: HList](m: Map[String, Any])(implicit
gen: LabelledGeneric.Aux[A, R],
fromMap: FromMap[R]
): Option[A] = fromMap(m).map(gen.from(_))
}
def to[A]: ConvertHelper[A] = new ConvertHelper[A]
以及示例:
case class Address(street: String, zip: Int)
case class Person(name: String, address: Address)
val mp = Map(
"name" -> "Tom",
"address" -> Map("street" -> "Jefferson st", "zip" -> 10000)
)
最后:
scala> to[Person].from(mp)
res0: Option[Person] = Some(Person(Tom,Address(Jefferson st,10000)))
这仅适用于成员为 Typeable
的 case class 或成员为 Typeable
或其他 case [=34] 的其他 case classes =]是的……(依此类推)。
陷阱
注意不要在导入中包含 scala.reflect.runtime.universe._
,因为这会破坏上述内容。
问题
val mp = Map("name" -> "Tom", "address" -> Map("street" -> "Jefferson st", "zip" -> 10000))
将其转换为 Person
的 class 大小写:
case class Person(name:String, address:Address)
case class Address(street:String, zip:Int)
val p = Person("Tom", Address("Jefferson st", 10000))
像这样:
val newP = mp.asCC[Person]
assert(newP.get == p)
我应该如何使用 Shapeless。
这是一个现成的、大部分未经测试的解决方案。首先是类型 class:
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
}
}
为了方便起见,还有一个助手class:
class ConvertHelper[A] {
def from[R <: HList](m: Map[String, Any])(implicit
gen: LabelledGeneric.Aux[A, R],
fromMap: FromMap[R]
): Option[A] = fromMap(m).map(gen.from(_))
}
def to[A]: ConvertHelper[A] = new ConvertHelper[A]
以及示例:
case class Address(street: String, zip: Int)
case class Person(name: String, address: Address)
val mp = Map(
"name" -> "Tom",
"address" -> Map("street" -> "Jefferson st", "zip" -> 10000)
)
最后:
scala> to[Person].from(mp)
res0: Option[Person] = Some(Person(Tom,Address(Jefferson st,10000)))
这仅适用于成员为 Typeable
的 case class 或成员为 Typeable
或其他 case [=34] 的其他 case classes =]是的……(依此类推)。
陷阱
注意不要在导入中包含 scala.reflect.runtime.universe._
,因为这会破坏上述内容。