我正在尝试使用此https://stackoverflow.com/a/31641779/1586965(How to use shapeless to convert generic Map[String, Any] to case class inside generic function?)来处理
case class Address(street: String, zip: Int)
case class PersonOptionalAddress(name: String, address: Option[Address])
我的测试失败:
"Convert Map to PersonOptionalAddress Some" in {
CaseClassFromMap[PersonOptionalAddress](Map(
"name" -> "Tom",
"address" -> Some(Map("street" -> "Jefferson st", "zip" -> 10000))
)) must_=== PersonOptionalAddress("Tom", Some(Address("Jefferson st", 10000)))
}
使用
java.util.NoSuchElementException: None.get
如果子结构未嵌套或为无,则测试正常。
我也尝试过这种方法,但是它也不起作用
"Convert Map to PersonOptionalAddress Some" in {
CaseClassFromMap[PersonOptionalAddress](Map(
"name" -> "Tom",
"address" -> Map("x" -> Map("street" -> "Jefferson st", "zip" -> 10000))
)) must_=== PersonOptionalAddress("Tom", Some(Address("Jefferson st", 10000)))
}
答案 0 :(得分:1)
如果您希望代码与PersonOptionalAddress
一起使用,则应再添加一个类型类的实例,以便它也可以与Map(
"name" -> "Tom",
"address" -> Some(Map ...)
)
一起使用
implicit def hconsFromMap0opt[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, Option[V]] :: T] =
new FromMap[FieldType[K, Option[V]] :: T] {
def apply(m: Map[String, Any]): Option[FieldType[K, Option[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](Some(gen.from(h))) :: t).orElse(for {
v <- m.get(witness.value.name)
r1 <- Typeable[Option[Map[String, Any]]].cast(v)
opt = for {
r <- r1
h <- fromMapH(r)
} yield gen.from(h)
t <- fromMapT(m)
} yield field[K](opt) :: t)
}
整个代码
import shapeless._
import labelled.{FieldType, field}
object App {
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
}
implicit def hconsFromMap0opt[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, Option[V]] :: T] =
new FromMap[FieldType[K, Option[V]] :: T] {
def apply(m: Map[String, Any]): Option[FieldType[K, Option[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](Some(gen.from(h))) :: t).orElse(for {
v <- m.get(witness.value.name)
r1 <- Typeable[Option[Map[String, Any]]].cast(v)
opt = for {
r <- r1
h <- fromMapH(r)
} yield gen.from(h)
t <- fromMapT(m)
} yield field[K](opt) :: 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
}
case class Address(street: String, zip: Int)
case class PersonOptionalAddress(name: String, address: Option[Address])
case class PersonAddress(name: String, address: Address)
def main(args: Array[String]): Unit = {
println(
CaseClassFromMap[PersonAddress](Map(
"name" -> "Tom",
"address" -> Map("street" -> "Jefferson st", "zip" -> 10000)
))
)//PersonAddress(Tom,Address(Jefferson st,10000))
println(
CaseClassFromMap[PersonOptionalAddress](Map(
"name" -> "Tom",
"address" -> Map("street" -> "Jefferson st", "zip" -> 10000)
))
)//PersonOptionalAddress(Tom,Some(Address(Jefferson st,10000)))
println(
CaseClassFromMap[PersonOptionalAddress](Map(
"name" -> "Tom",
"address" -> Some(Map("street" -> "Jefferson st", "zip" -> 10000))
))
)//PersonOptionalAddress(Tom,Some(Address(Jefferson st,10000)))
println(
CaseClassFromMap[PersonOptionalAddress](Map(
"name" -> "Tom",
"address" -> None
))
)//PersonOptionalAddress(Tom,None)
}
}