Вот откладывание, в основном непроверенное решение. Во-первых для класса типа:
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 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
или другие классы случаев, членами которых являются либо Typeable
или другие тематические классы ... (и так далее).
Изменив пример следующим образом, он не работает. Есть идеи? 'случай класс Адрес (улица: String, почтовый индекс: Int, состояние: String)' Вал пл = Map ("название" ' -> "Том", "адрес" -> Map ("улица" -> «Jefferson st», «zip» -> 10000, «state» -> «CA») ) ' – lambdista
@ lambdista Это была проблема расхождения - я только что отредактировал ответ, чтобы быть более надежным. –
@TravisBrown Как это можно адаптировать, чтобы некоторые из значений на Карте были «нулевыми». Например, '' zip "-> null'. – ISJ