2015-11-05 12 views
5

sto cercando di astrarre il android.os.Bundle API, con l'obiettivo di generare pacchetti in questo modo:Trasformare i dati da una memoria runtime per una classe case

case class MyClass(a: Int, b: String) 
val mc = MyClass(3, "5") 
implicit val bundleable = Bundle.from[MyClass]() 
val bundle = bundleable.write(mc) 
assert(mc == bundleable.read(bundle)) 

Conversione della classe caso ad un LabelledGeneric e scrivere la chiave coppie di valori allo Bundle è semplice. Ma non riesco a trovare un modo per estrarre i valori da un Bundle nel loro tipo originale. Immagino che le numerose librerie JSON là fuori abbiano già risolto questo problema, ma non riesco ancora a trovare un indizio su come procedere.

object Bundle { 
    def from[T] = new { 
     def apply[LG <: HList, K <: HList, N <: Nat]()(
      implicit 
      lg: LabelledGeneric.Aux[T, LG], 
      l: Length.Aux[LG, N], 
      k: Keys.Aux[LG, K], 
      lfw: LeftFolder.Aux[LG, Bundle, fold.write.type, Bundle], 
      //lfr: LeftFolder.Aux[K, Bundle, fold.read.type, LG], 
      ti: ToInt[N] 
     ) = new Bundleable[T] { 
      override def write(value: T): Bundle = { 
       lg.to(value).foldLeft(new Bundle(toInt[N]))(fold.write) 
      } 

      override def read(bundle: Bundle): T = ??? 
     } 
    } 

    object fold { 
     object write extends Poly2 { 
      implicit def default[K <: Symbol, V: Bundleize](implicit key: Witness.Aux[K]): Case.Aux[Bundle, FieldType[K, V], Bundle] = { 
       at { (bundle, value) ⇒ 
        implicitly[Bundleize[V]].write(key.value.name, value, bundle) 
        bundle 
       } 
      } 
     } 

     object read extends Poly2 { 
      ??? 
     } 
    } 
} 

/** 
* Read or write a single value from/into a Bundle 
*/ 
trait Bundleize[T] { 
    def read(key: String, bundle: Bundle): T 

    def write(key: String, value: T, bundle: Bundle): Unit 
} 

/** 
* Transformation T <> Bundle 
*/ 
trait Bundleable[T] { 
    def read(bundle: Bundle): T 

    def write(value: T): Bundle 
} 

Inoltre, c'è un modo per ristrutturare il codice in modo tale, che posso scrivere Bundle.from[MyClass], piuttosto che Bundle.from[MyClass]() (omettendo le parentesi)?

+1

Hai ragione a pensare a questo come un problema di serializzazione (de), dove fasci sono l'obiettivo di serializzazione piuttosto che JSON. Un esempio che potrebbe aiutarti a procedere è [questo] (https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/sexp.scala#L34) che ha come target un S-expresssion like representation ... dovresti essere in grado di adattare questo per i bundle. –

+1

Grazie mille, non posso credere quanto sia stato facile. La parte più difficile è stata capire cosa ha in comune il codice di esempio S-expression con il mio esempio. L'implementazione funzionante si è ridotta a copia e incolla. Pubblicherò una soluzione di lavoro pulita quando ho finito. – Taig

risposta

7

Grazie al puntatore dell'esempio S-Expression senza forma, sono riuscito a mettere insieme una soluzione funzionante.

import android.os.Bundle 
import shapeless.labelled._ 
import shapeless.ops.hlist.{ Length, LeftFolder } 
import shapeless._ 
import shapeless.Nat.toInt 
import shapeless.ops.nat.ToInt 
import shapeless.syntax.std.tuple._ 

/** 
* Type class that instructs how to deserialize/serialize a value from/to a Bundle 
*/ 
trait Bundleable[T] { 
    def read(bundle: Bundle): T 

    def write(value: T): Bundle 
} 

object Bundleable { 
    def apply[T](r: Bundle ⇒ T, w: T ⇒ Bundle) = new Bundleable[T] { 
     override def read(bundle: Bundle) = r(bundle) 

     override def write(value: T) = w(value) 
    } 

    def from[T: Bundleable]: Bundleable[T] = the[Bundleable[T]] 

    private object fold { 
     object write extends Poly2 { 
      implicit def default[K <: Symbol, V: Bundleize](implicit key: Witness.Aux[K]) = { 
       at[Bundle, FieldType[K, V]] { (bundle, value) ⇒ 
        implicitly[Bundleize[V]].write(key.value.name, value, bundle) 
        bundle 
       } 
      } 
     } 
    } 

    implicit val `Bundleable[HNil]` = Bundleable[HNil](_ ⇒ HNil, _ ⇒ Bundle.EMPTY) 

    implicit def `Bundleable[HList]`[K <: Symbol, V, T <: HList, N <: Nat](
     implicit 
     key: Witness.Aux[K], 
     bv: Bundleize[V], 
     bt: Bundleable[T], 
     l: Length.Aux[FieldType[K, V] :: T, N], 
     ti: ToInt[N], 
     lf: LeftFolder.Aux[FieldType[K, V] :: T, Bundle, fold.write.type, Bundle] 
    ) = Bundleable[FieldType[K, V] :: T](
     bundle ⇒ field[K](bv.read(key.value.name, bundle)) :: bt.read(bundle), 
     _.foldLeft(new Bundle(toInt[N]))(fold.write) 
    ) 

    implicit def `Bundleable[LabelledGeneric]`[T, LG](
     implicit 
     lg: LabelledGeneric.Aux[T, LG], 
     b: Bundleable[LG] 
    ) = Bundleable[T](bundle ⇒ lg.from(b.read(bundle)), value ⇒ b.write(lg.to(value))) 
} 

utilizzo Esempio:

case class MyCaseClass(a: String, b: Int, c: Double) 
val instance = MyCaseClass("3", 3, 3) 
val bundleable = Bundleable.from[MyCaseClass] 
val bundle: Bundle = bundleable.write(instance) 
val deserialized = bundleable.read(bundle) 
assert(instance == deserialized) 
Problemi correlati