简体   繁体   中英

How to convert Map to case class (recursively)

How to convert Map with nested Map (with key contains name of nested case clas) to case class.

Assuming we have:

case class OuterClass(fieldName1: String, fieldName2: InnerClass)
case class InnerClass(innerFieldName: Int)

How to convert:

Map("fieldName1" -> "stringValue", 
    "name" -> "ClassName", 
    "fieldName2" -> Map(
         "innerFieldName" -> "value", 
         "innerClassName" -> "CaseClassName"))

to the

OuterClass("stringValue", InnerClass("value"))

?

You can use mirroring :

 import scala.reflect.runtime.universe._     
 val ru = scala.reflect.runtime.universe

 val m = runtimeMirror(getClass.getClassLoader)

 def constructor(t: Type) = //get MethodMirror for constructor
     m.reflectClass(t.typeSymbol.asClass).reflectConstructor(t.decl(nme.CONSTRUCTOR).asMethod)

 def create(t: Type, inputRaw: Map[String, Any]): Any = {
     val cnstrct = constructor(t)
     val params = cnstrct.symbol.paramLists.flatten //get constructor params
     val input = inputRaw map { //resolve internal maps
        case (k: String, m: Map[String, Any]) => 
           k -> create(params.find(_.name.toString == k).get.typeSignature, m) 
        case x => x
     }
     cnstrct(params.map(_.name.toString).map(input).toSeq: _*) //invoke constructor
 }


scala> case class Aaa(aa: Int, bb: String)
defined class Aaa

scala> case class Zzz(a: Aaa, b: String)
defined class Zzz

scala> create(typeOf[Zzz], Map("a" -> Map("aa" -> 1, "bb" -> "2"), "b" -> "b"))
res22: Any = Zzz(Aaa(1,2),b)

Note, that you don't need innerClassName field here. If you don't know the name of root class:

create(m.classSymbol(Class.forName("RootCaseClassName")).selfType, ...)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM