Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting the parameters of a case class through Reflection

As a follow up of Matt R's question, as Scala 2.10 has been out for quite an amount of time, what would be the best way to extract the fields and values of a case class. Taking a similar example:

case class Colour(red: Int, green: Int, blue: String) {
  val other: Int = 42
} 

val RBG = Colour(1,3,"isBlue")

I want to get a list (or array or any iterator for that matter) that would have the fields declared in the constructor as tuple values like these:

[(red, 1),(green, 3),(blue, "isBlue")]

I know the fact that there are a lot of examples on the net regarding the same issue but as I said, I wanted to know what should be the most ideal way to extract the required information

like image 477
Core_Dumped Avatar asked Dec 19 '22 20:12

Core_Dumped


1 Answers

If you use Scala 2.10 reflection, this answer is half of the things you need. It will give you the method symbols of the case class, so you know the order and names of arguments:

import scala.reflect.runtime.{universe => ru}
import ru._

def getCaseMethods[T: TypeTag] = typeOf[T].members.collect {
  case m: MethodSymbol if m.isCaseAccessor => m
}.toList

case class Person(name: String, age: Int)

getCaseMethods[Person]  // -> List(value age, value name)

You can call .name.toString on these methods to get the corresponding method names.

The next step is to invoke these methods on a given instance. You need a runtime mirror for that

val rm = runtimeMirror(getClass.getClassLoader)

Then you can "mirror" an actual instance:

val p  = Person("foo", 33)
val pr = rm.reflect(p)

Then you can reflect on pr each method using reflectMethod and execute it via apply. Without going through each step separately, here is a solution altogether (see the val value = line for the mechanism of extracting a parameter's value):

def caseMap[T: TypeTag: reflect.ClassTag](instance: T): List[(String, Any)] = {
  val im = rm.reflect(instance)
  typeOf[T].members.collect {
    case m: MethodSymbol if m.isCaseAccessor =>
      val name  = m.name.toString
      val value = im.reflectMethod(m).apply()
      (name, value)
  } (collection.breakOut)
}

caseMap(p) // -> List(age -> 33, name -> foo)
like image 86
0__ Avatar answered May 12 '23 23:05

0__