6
votes

i use shapeless for case class conversion, i have a 2 case class:

import shapeless._

case class Foo(id: Int, name: String)
case class Bar(id: Int, name: String, price: Double)

val fooGen = Generic[Foo]
val barGen = Generic[Bar]

val foo = Foo(1, "foo")
val fooRepr = fooGen.to(foo)
val additional = fooRepr :+ 1.0
val bar = barGen.from(additional)

This works fine, but when i try convert Bar to Foo

fooGen.from(barGen.to(bar))

i get an error:

found   : main.barGen.Repr
[error]     (which expands to)  shapeless.::[Int,shapeless.::    [String,shapeless.::[Double,shapeless.HNil]]]
[error]  required: main.fooGen.Repr
[error]     (which expands to)  shapeless.::[Int,shapeless.::[String,shapeless.HNil]]
[error]   println(fooGen.from(barGen.to(bar)))    

Is it possible convert one case class where more fields than in another?

2

2 Answers

11
votes

Similar to how you adjust the HList representation of Foo, by adding an element, you'd have to adjust the HList representation of Bar as well, by removing the extra element:

fooGen.from(barGen.to(bar).take(2))

take takes a Nat argument, and this line of code uses the implicit conversion from an Int literal to a Nat type-level natural number.

You can find other methods available on HLists in shapeless.syntax.hlists.scala.

4
votes

My answer is based on a previous answer by Travis Brown, for a somewhat similar question.

import shapeless._, ops.hlist._, ops.record._

class SameFieldConverter[T] {
    def apply[S, SR <: HList, TR <: HList, MR <: HList, IR <: HList](s: S)(implicit
      genS: LabelledGeneric.Aux[S, SR],
      genT: LabelledGeneric.Aux[T, TR],
      merger: Merger.Aux[SR, HNil, MR],
      intersection: Intersection.Aux[MR, TR, IR],
      align: Align[IR, TR]) = genT.from(intersection(merger(genS.to(s), HNil)))
  }
// defined class SameFieldConverter

def convertTo[T] = new SameFieldConverter[T]
//defined function convertTo

case class Foo(one: String, two: Int, three: Boolean)
// defined class Foo

case class Bar(three: Boolean, one: String)
// defined class Bar

convertTo[Bar](Foo("One", 2, false))
// res26: Bar = Bar(false, "One")