scala shapeless

scala - Conversión de un mapa[String, Any] a una clase de caso usando Shapeless



(1)

Aquí hay una solución sin prueba, en su mayoría no probada. Primero para la clase de tipo:

import shapeless._, labelled.{ FieldType, field } trait FromMap[L <: HList] { def apply(m: Map[String, Any]): Option[L] }

Y luego las instancias:

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 } }

Y luego una clase de ayuda por conveniencia:

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]

Y el ejemplo:

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) )

Y finalmente:

scala> to[Person].from(mp) res0: Option[Person] = Some(Person(Tom,Address(Jefferson st,10000)))

Esto solo funcionará para las clases de casos cuyos miembros son Typeable u otras clases de casos cuyos miembros son Typeable u otras clases de casos ... (y así sucesivamente).

Gotcha

scala.reflect.runtime.universe._ cuidado de no tener scala.reflect.runtime.universe._ en sus importaciones, ya que esto romperá lo anterior.

La pregunta here refiere a la asignación de una clase de caso a un Mapa [Cadena, Cualquiera]. Me preguntaba qué sería al revés, convirtiendo Map [String, Any] en una clase de caso. Dado el siguiente mapa:

val mp = Map("name" -> "Tom", "address" -> Map("street" -> "Jefferson st", "zip" -> 10000))

Convertirlo en una clase de caso de Person :

case class Person(name:String, address:Address) case class Address(street:String, zip:Int) val p = Person("Tom", Address("Jefferson st", 10000))

con algo como esto:

val newP = mp.asCC[Person] assert(newP.get == p)

¿Cómo debo hacer eso con Shapeless?