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
}
}
Author by
J Pullar
Updated on June 18, 2022Comments
-
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 about 11 yearsAh 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 about 11 yearsWait, no got it currentMirror.reflect(someCaseClass).symbol.asType.typeSignature.members
-
Randall Schulz over 10 yearsI'm finding cases where this fails with
scala.ScalaReflectionException: <none> is not a term
in the expressionconstructorSymbol.asTerm.alternatives
. The documentation comment fordeclaration
refers toOverloadedSymbol
, but no such entity seems to exist. -
Randall Schulz over 10 yearsIt 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 about 10 yearsCan this work with multi-thread environment under scala 2.10 ?
-
som-snytt over 9 yearsAlso,
case class P(i: Int)(j: Int)
. -
samthebest over 9 yearsAny way to make this return the methods in the same order they where declared??
-
Travis Brown over 9 years
-
WestCoastProjects almost 5 yearsThis would be handy to be directly part of
case class
es -
WestCoastProjects almost 5 yearsI can't get this to compile due to
Symbols
is aTrait
so its components - specifiallyMethodSymbol
- can not be imported. Can you expand the example to include the imports ofSymbols
andMethodSymbol
? -
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.