2015-07-26 17 views
17

'u kullanarak bir vaka sınıfına Eşleme [String, Any] bir dosya sınıfına Dönüştürme here sorusu, bir Case sınıfının Map [String, Any] ile eşlenmesini soruyor. Harita [String, Any] 'ı bir vaka sınıfına dönüştürmenin başka yolun ne olacağını merak ediyordum.Shapeless

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

Person bir vaka sınıfa dönüştürme::

case class Person(name:String, address:Address) 
case class Address(street:String, zip:Int) 

val p = Person("Tom", Address("Jefferson st", 10000)) 

şey ile böyle:

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

nasıl şekilsiz ile gerçekleştirmeliyiz şu haritayı önüne alındığında.

cevap

23

İşte çoğu kez denenmemiş bir çözüm. Sonra

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

Ve yardımcı kolaylık sınıfı::

import shapeless._, labelled.{ FieldType, field } 

trait FromMap[L <: HList] { 
    def apply(m: Map[String, Any]): Option[L] 
} 

Sonra örneklerini:

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] 

Ve örnek:

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) 
) 
tip sınıf için İlk

Son olarak:

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

Bu yalnızca üyeleri üyeleri ya Typeable veya başka vaka sınıfları, ya Typeable veya başka vaka sınıfları vardır vaka sınıfları için çalışacaktır ... (ve benzeri).

+0

Örneğin aşağıdaki şekilde değiştirilmesi işe yaramıyor gibi gözüküyor. Herhangi bir fikir? 'vaka sınıf Adresi (sokak: String, zip: Int, devlet: String)' 'val mp = Harita ( "adı" -> "Tom", "adres" -> Harita ("sokak" -> "Jefferson st", "zip" -> 10000, "state" -> "CA") – lambdista

+0

@lambdista Bir sapma meselesiydi - daha sağlam olması için cevabı düzenledim. –

+0

@TravisBrown Haritanın bazı değerlerinin "boş" olmasına izin vermek için bu nasıl uyarlanabilir? Örneğin, "zip" -> null'. – ISJ