2015-11-05 12 views
5

ich zu abstrakt über den android.os.Bundle API versucht, mit dem Ziel Bundles in dieser Art und Weise zu erzeugen:Umsetzen von Daten aus einer Laufzeitspeicher zu einem Fall Klasse

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

Konvertieren die Fallklasse zu einem LabelledGeneric und den Schlüssel zu schreiben Wertepaare zu Bundle ist einfach. Aber ich kann keinen Weg finden, die Werte von einem Bundle zurück in ihren ursprünglichen Typ zu extrahieren. Ich denke, dass die zahlreichen JSON-Bibliotheken, die da draußen sind, dieses Problem bereits gelöst haben, aber ich scheitere immer noch darin, einen Anhaltspunkt dafür zu finden, wie es weitergehen soll.

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 
} 

Auch ist es eine Möglichkeit, den Code so restrukturieren, dass ich Bundle.from[MyClass] schreiben kann, anstatt Bundle.from[MyClass]() (die Klammern weggelassen)?

+1

Du hast Recht dies als (de) Serialisierung Problem zu denken, wo Bündel die Serialisierung Ziel sind eher dann JSON. Ein Beispiel, das Ihnen beim Fortfahren helfen könnte, ist [dieses] (https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/sexp.scala#L34), das auf ein Ziel abzielt S-Expression-ähnliche Darstellung ... Sie sollten in der Lage sein, dies für Bündel anzupassen. –

+1

Vielen Dank, ich kann nicht glauben, wie einfach das eigentlich war. Der schwierigste Teil war, herauszufinden, was der S-Ausdruck-Beispielcode mit meinem Beispiel gemeinsam hat. Die Arbeitsumsetzung ging bis zum schamlosen Kopieren & Einfügen verloren. Ich werde eine saubere Arbeitslösung posten, wenn ich fertig bin. – Taig

Antwort

7

Dank dem Hinweis auf das formlose S-Expression-Beispiel konnte ich eine funktionierende Lösung zusammenstellen.

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

Verwendungsbeispiel:

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)