2015-11-05 11 views
5

私はこの方法でバンドルを生成することを目指し、android.os.Bundle APIの上に抽象化しようとしています:ケースクラスにランタイムストレージからデータを変換する

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

LabelledGenericにケースクラスを変換し、キーを書きます値のペアはBundleに簡単になります。しかし、Bundleの値を元のタイプに戻す方法は見つけられません。私はすでにそこにある数多くのJSONライブラリがこの問題を解決したと思いますが、それでも進行する方法の手がかりを見つけることができません。

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 
} 

また、このような方法でコードを再構築する方法は私はBundle.from[MyClass]()(括弧を省略)のではなく、Bundle.from[MyClass]を書き込むことができることは、ありますか?

+1

あなたがいうし、バンドルがシリアライズ対象としている(デ)シリアライズの問題、JSONと考えることが正しいです。先に進むのを助ける例は、[this one](https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/sexp.scala#L34)です。表現のようなS表現...バンドルにこれを適応させることができるはずです。 –

+1

本当にありがとう、私は実際にはいかに簡単か信じられません。最も難しい部分は、S式のサンプルコードが私の例と共通しているものを理解することでした。作業の実装は、恥知らずのコピー&ペーストまで煮詰まった。私が終わったら、きれいな解決策を投稿します。 – Taig

答えて

7

シェイプレスなS-Expressionの例へのポインタのおかげで、私は実用的なソリューションをまとめることができました。

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

使用例:

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) 
関連する問題