Scala 2.10 reflection, how do I extract the field values from a case class, i.e. field list from case class

13,861

Solution 1

MethodSymbol has an isCaseAccessor method that allows you to do precisely this:

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

Now you can write the following:

scala> case class Person(name: String, age: Int)
defined class Person

scala> getMethods[Person]
res1: List[reflect.runtime.universe.MethodSymbol] = List(value age, value name)

And you get only the method symbols you want.

If you just want the actual field name (not the value prefix) and you want them in the same order then:

def getMethods[T: TypeTag]: List[String] =
  typeOf[T].members.sorted.collect {
    case m: MethodSymbol if m.isCaseAccessor => m.name.toString
  }

Solution 2

If you want to get fancier you can get them in order by inspecting the constructor symbol. This code works even if the case class type in question has multiple constructors defined.

  import scala.collection.immutable.ListMap
  import scala.reflect.runtime.universe._

  /**
    * Returns a map from formal parameter names to types, containing one
    * mapping for each constructor argument.  The resulting map (a ListMap)
    * preserves the order of the primary constructor's parameter list.
    */
  def caseClassParamsOf[T: TypeTag]: ListMap[String, Type] = {
    val tpe = typeOf[T]
    val constructorSymbol = tpe.decl(termNames.CONSTRUCTOR)
    val defaultConstructor =
      if (constructorSymbol.isMethod) constructorSymbol.asMethod
      else {
        val ctors = constructorSymbol.asTerm.alternatives
        ctors.map(_.asMethod).find(_.isPrimaryConstructor).get
      }

    ListMap[String, Type]() ++ defaultConstructor.paramLists.reduceLeft(_ ++ _).map {
      sym => sym.name.toString -> tpe.member(sym.name).asMethod.returnType
    }
  }
Share:
13,861
J Pullar
Author by

J Pullar

Updated on June 18, 2022

Comments

  • J Pullar
    J Pullar almost 2 years

    How can I extract the field values from a case class in scala using the new reflection model in scala 2.10? For example, using the below doesn't pull out the field methods

      def getMethods[T:TypeTag](t:T) =  typeOf[T].members.collect {
        case m:MethodSymbol => m
      }
    

    I plan to pump them into

      for {field <- fields} {
        currentMirror.reflect(caseClass).reflectField(field).get
      }
    
  • J Pullar
    J Pullar about 11 years
    Ah I realise now that my approach was wrong. Any idea how to get the caseAccessors from an unknown case class? IE one which is currently stored in as val SomeCaseClass:Any
  • J Pullar
    J Pullar about 11 years
    Wait, no got it currentMirror.reflect(someCaseClass).symbol.asType.typeSigna‌​ture.members
  • Randall Schulz
    Randall Schulz over 10 years
    I'm finding cases where this fails with scala.ScalaReflectionException: <none> is not a term in the expression constructorSymbol.asTerm.alternatives. The documentation comment for declaration refers to OverloadedSymbol, but no such entity seems to exist.
  • Randall Schulz
    Randall Schulz over 10 years
    It turns out that that was happening 'cause I was calling it with this.type from a trait used as a supertype of the case class in question.
  • jilen
    jilen about 10 years
    Can this work with multi-thread environment under scala 2.10 ?
  • som-snytt
    som-snytt over 9 years
    Also, case class P(i: Int)(j: Int).
  • samthebest
    samthebest over 9 years
    Any way to make this return the methods in the same order they where declared??
  • Travis Brown
    Travis Brown over 9 years
    @samthebest: Yes! Just add .sorted after members (see the MemberScope docs for details).
  • WestCoastProjects
    WestCoastProjects almost 5 years
    This would be handy to be directly part of case classes
  • WestCoastProjects
    WestCoastProjects almost 5 years
    I can't get this to compile due to Symbols is a Trait so its components - specifially MethodSymbol - can not be imported. Can you expand the example to include the imports of Symbols and MethodSymbol ?
  • Travis Brown
    Travis Brown almost 5 years
    @javadba This is a six year old answer and I'm not terribly interested in updating it myself, but feel free to edit—it probably just needs a scala.reflect.runtime.universe._ import.