Search code examples
scalamapscase-class

case class from Map


I'm trying to implement (de)serialization from Scala case classes to Maps (field_name -> field_value). The problem is I don't know how to create a function, that would take a case class type and a map, instantiate, populate and return it. I've seen this done in some JSON libraries, but I don't get the code.


Solution

  • Doing this for a particular case class is trivial:

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    case class Foo(x: Int, y: Double, z: String) {
      def toMap = Map('x -> x, 'y -> y, 'z -> z)
    }
    object Foo {
      def apply(vals: Map[Symbol, Any]): Foo = Foo(vals('x).asInstanceOf[Int], vals('y).asInstanceOf[Double], vals('z).asInstanceOf[String])
    }
    
    // Exiting paste mode, now interpreting.
    
    defined class Foo
    defined module Foo
    
    scala> Foo(Map('x -> 1, 'y -> 2.0, 'z -> "wibble"))
    res0: Foo = Foo(1,2.0,wibble)
    
    scala> res0.toMap
    res1: scala.collection.immutable.Map[Symbol,Any] = Map('x -> 1, 'y -> 2.0, 'z -> wibble)
    

    But I'm guessing that you want to create something that works for any case class, where you don't necessarily know what the names of the values are?

    In which case, you're going to have to take a look at reflection (added in Scala 2.10.0):

    http://docs.scala-lang.org/overviews/reflection/overview.html

    You can use reflection to iterate over the members of a class, find out what their names are and then create your map. For example:

    scala> import reflect.runtime.universe._
    import reflect.runtime.universe._
    
    scala> typeOf[Foo].members filter { m => m.isMethod && m.asMethod.isStable }
    res2: Iterable[reflect.runtime.universe.Symbol] = SynchronizedOps(value z, value y, value x)