2017-03-29 1 views
0

J'ai donc une implémentation fonctionnelle, sauf que je ne peux pas obtenir ma liste HList dans le bon ordre avant d'utiliser LabelledGenerics pour construire l'objet CaseClass.Mapper [String, Array [Byte]] à CaseClass avec Scala Shapeless

Ce qui fonctionne est en ce moment

case class Foo(a:int, b:String) 
val serializedRecord("a" -> somebytes, "b" -> someotherbytes) 
val caseClassObject = HBaseSerDe[Foo].deserialize(serializedRecord) 

Ce qui ne fonctionne pas est:

case class Foo(a:int, b:String) 
val serializedRecord(x -> value1 ,"a" -> somebytes, "b" -> someotherbytes, z -> value2) 
//expected Foo(somebytes, someotherbytes) 
val caseClassObject = HBaseSerDe[Foo].deserialize(serializedRecord) 

D'après ce que j'ai lu que je suis censé utiliser Aligner et Intersection pour obtenir mon Hlist triée avant désérialisation mais dans tous les exemples que je pouvais trouver quelque chose qu'ils utilisent comme ce qui suit (extrait de github.com/underscoreio/shapeless-guide)

implicit def genericMigration[ 
    A, B, 
    ARepr <: HList, BRepr <: HList, 
    Unaligned <: HList 
](
    implicit 
    aGen : LabelledGeneric.Aux[A, ARepr], 
    bGen : LabelledGeneric.Aux[B, BRepr], 
    inter : hlist.Intersection.Aux[ARepr, BRepr, Unaligned], 
    align : hlist.Align[Unaligned, BRepr] 
): Migration[A, B] = new Migration[A, B] { 
    def apply(a: A): B = 
     bGen.from(align.apply(inter.apply(aGen.to(a)))) 
} 

Cependant, je n'ai pas de CaseClass A et je ne pouvais pas comprendre comment construire sa représentation à la volée. Ci-dessous vous pouvez trouver le code actuel:

implicit val hNilEncoder = instance[HNil](
    r => Map(), 
    l => HNil 
) 

    implicit def hListSerDe[ 
     Key <: Symbol, 
     H, 
     T <: HList 
    ](
    implicit 
    key: Witness.Aux[Key], 
    headSerDe: HBaseFieldShapelessSerDe[H], 
    tailSerDe: HBaseRecordShapelessSerDe[T] 
    ): HBaseRecordShapelessSerDe[FieldType[Key, H] :: T] = 
    instance[FieldType[Key, H] :: T](
     hList => { 
     Map(key.value.name -> headSerDe.serialize(hList.head)) ++ tailSerDe.serialize(hList.tail) 
     }, 
     byteList => { 
     field[Key](headSerDe.deSerialize(byteList.head._2)) :: tailSerDe.deSerialize(byteList.tail) 
     } 
    ) 

    implicit def caseClassSerDe[ 
     TargetType, 
     L <: HList 
    ](
    implicit 
    genRepr: LabelledGeneric.Aux[TargetType, L], 
    hListSerDe: HBaseRecordShapelessSerDe[L] 
    ): HBaseRecordShapelessSerDe[TargetType] = 
    instance[TargetType](
     hList => { 
     hListSerDe.serialize(genRepr.to(hList)) 
     }, 
     byteList => { 

     genRepr.from(hListSerDe.deSerialize(byteList)) 
     } 
    ) 


    def apply[RecordSerDeType](implicit serde: HBaseRecordShapelessSerDe[RecordSerDeType]): HBaseRecordShapelessSerDe[RecordSerDeType] = serde 

    def instance[RecordSerDeType](serializeFunc: RecordSerDeType => Map[String, Array[Byte]], deserializeFunc: Map[String, Array[Byte]] => RecordSerDeType): HBaseRecordShapelessSerDe[RecordSerDeType] = { 
    new HBaseRecordShapelessSerDe[RecordSerDeType] { 
     override def serialize(value: RecordSerDeType): Map[String, Array[Byte]] = serializeFunc(value) 

     override def deSerialize(value: Map[String, Array[Byte]]): RecordSerDeType = deserializeFunc(value) 
    } 
    } 

Répondre

0

Je l'ai résolu maintenant et voici la solution pour la postérité. Avec le recul, c'était assez simple.

import shapeless.labelled.FieldType 
import shapeless.{::, Generic, HList, HNil, LabelledGeneric, Witness} 
import shapeless.labelled.{FieldType, _} 


object MapToCaseClass { 


    sealed trait RecordConverter[V, TargetType] { 
    def convert(rawRecord: Map[String,V]):TargetType 
    } 


    implicit def hNilConverter[V]:RecordConverter[V, HNil] = instance[V,HNil](
    _ => HNil 
) 

    implicit def hListConverter[V, H, T <: HList, K <: Symbol](implicit fieldConverter: FieldConverter[V, H], tailConverter:RecordConverter[V, T], key: Witness.Aux[K]):RecordConverter[V, FieldType[K, H] :: T] = 
    instance[V, FieldType[K, H] :: T](
     fieldMap => { 
     val currentKeyName = key.value.name 
     val value = fieldMap(currentKeyName) 
     val mapRest = fieldMap.filterKeys(_!=currentKeyName) 
     //TODO: Add a way for default value providing, Option handling and nested HLists for in case no value was found 
     field[K](fieldConverter.convert(value)) :: tailConverter.convert(mapRest) 
     } 
    ) 

    implicit def caseClassConverter[V, C, CRepr <: HList](implicit caseClassGenRepr: LabelledGeneric.Aux[C,CRepr], recordConverter:RecordConverter[V, CRepr]):RecordConverter[V, C] = 
    instance[V, C](
     fieldMap => { 
     caseClassGenRepr.from(recordConverter.convert(fieldMap)) 
     } 
    ) 

    private[this] def instance[V, T](func: Map[String,V] => T) : RecordConverter[V, T] = new RecordConverter[V, T] { 
    override def convert(rawRecord: Map[String, V]): T = func(rawRecord) 
    } 

    //summoner 
    def apply[V, T](implicit recordConverter: RecordConverter[V, T]):RecordConverter[V, T] = recordConverter 


}