如何将Map转换为case类(递归)

时间:2014-12-23 01:51:57

标签: scala dictionary reflection case-class

如何使用嵌套Map(带键包含嵌套case clas的名称)将Map转换为case类。

假设我们有:

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

如何转换:

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

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

1 个答案:

答案 0 :(得分:2)

您可以使用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)

请注意,您在这里不需要innerClassName字段。如果您不知道根类的名称:

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