Converting data from storage at runtime to case class

I am trying to abstract from the android.os.Bundle API, trying to generate Bundles this way:

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

Converting the case class to LabelledGeneric and writing key value pairs to the Bundle is simple. But I cannot find a way to extract the values โ€‹โ€‹from the Bundle back to the original type. I think that numerous JSON libraries have already solved this problem, but I still canโ€™t find the key to how to proceed.

 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 } 

Also, is there a way to restructure the code in such a way that I can write Bundle.from[MyClass] and not Bundle.from[MyClass]() (omitting the parentheses)?

+5
source share
1 answer

Thanks to the pointer to the formless S-Expression example, I was able to put together a working solution.

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

Sample Usage:

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

Source: https://habr.com/ru/post/1235258/


All Articles