Get case class field's name and type with shap

2019-08-16 08:40发布

问题:

Is it possible to get scala case class field's names and types with shapeless?

I've tried like this (T is case class):

trait Cpo[T] {

def withPrimaryKey[R <: HList, K, V <: HList](f: Seq[Symbol] => Seq[Symbol])(
    implicit labellGeneric: LabelledGeneric.Aux[T, R], keys: Keys.Aux[R, K],
    ktl: hlist.ToList[K, Symbol]): Cpo[T]
}

but I only can get field's name.

Zlaja

回答1:

Try

object typeablePoly extends Poly1 {
  implicit def default[A](implicit typeable: Typeable[A]): Case.Aux[A, String] = at(_ => typeable.describe)
}

trait Cpo[T] {

  def withPrimaryKey[R <: HList, K <: HList, V <: HList, V1 <: HList](f: Seq[Symbol] => Seq[Symbol])(implicit
    labellGeneric: LabelledGeneric.Aux[T, R],
    keys: Keys.Aux[R, K],
    ktl: hlist.ToList[K, Symbol],
    values: Values.Aux[R, V],
    mapper: hlist.Mapper.Aux[typeablePoly.type, V, V1],
    vtl: hlist.ToList[V1, String]
  ): Cpo[T] 
}

Now ktl gives list of field names (as Symbols) and vtl gives list of field types (as Strings).


Try

  object typeablePoly extends Poly1 {
    implicit def default[A](implicit typeable: Typeable[A]): Case.Aux[A, String] = at(_ => typeable.describe)
  }

  object nullPoly extends Poly0 {
    implicit def default[A]: ProductCase.Aux[HNil, A] = at(null.asInstanceOf[A])
  }

  trait Cpo[T] {

    def withPrimaryKey[R <: HList, K <: HList, V <: HList, V1 <: HList](f: Seq[Symbol] => Seq[Symbol])(implicit
      labellGeneric: LabelledGeneric.Aux[T, R],
      keys: Keys.Aux[R, K],
      ktl: hlist.ToList[K, Symbol],
      values: Values.Aux[R, V],
      mapper: hlist.Mapper.Aux[typeablePoly.type, V, V1],
      fillWith: hlist.FillWith[nullPoly.type, V],
      vtl: hlist.ToList[V1, String]
    ): Cpo[T] = {
      println(ktl(keys())) // List('i, 's)
      println(vtl(mapper(fillWith()))) // List(Int, String)
      ???
    }
  }

  case class MyClass(i: Int, s: String)
  new Cpo[MyClass] {}.withPrimaryKey(identity)


回答2:

You definitely can get field names. For example, here you can find how to write your shapeless-based generic derivation mechanism: Bits of shapeless part 2. More specific, you should look at Deriving case Classes part, there is a function which derives encoder for arbitrary case class, its signature is:

implicit def hconsToJson[Key <: Symbol, Head, Tail <: HList](
    implicit key: Witness.Aux[Key],
    headWrites: JsonWrites[Head],
    tailWrites: JsonWrites[Tail])
    : JsonWrites[FieldType[Key, Head] :: Tail] = ???

Hence, key parameter allows you to acces the field name of the certain field. For the types, the only known way to me is to use reflection. Read this for details Scala manual on type tags.



回答3:

If using shapeless is not necessary, you can get the type as well as value using Product class in scala

case class Test(x:Int,y:String,z:Boolean)
println(getGeyNameValueType(Test(1,"a",true)).foreach(println))

def getGeyNameValueType(inp: Product): Iterator[(Any, Class[_])] = {
    val itr = inp.productIterator
    for {
      item <- itr
    } yield (item, item.getClass)
}

The output is

(1,class java.lang.Integer)
(a,class java.lang.String)
(true,class java.lang.Boolean)
()