reflection - Scala Map to Case Class conversion -
i'm trying convert scala map[string, any] case class using scala reflection (scala 2.11) follows -
val m = map("name" -> "abc", "age" -> 7, "gender" -> "male") case class somecc(name: string, age: int, gender: string) import scala.reflect.classtag def createcaseclass[t](somemap : map[string, any])(implicit someclasstag : classtag[t]) = { val ctor = someclasstag.runtimeclass.getconstructors.head val args = someclasstag.runtimeclass.getdeclaredfields.map(x => somemap(x.getname)) ctor.newinstance(args: _*).asinstanceof[t] }
this unfortunately results in compile error -
name: unknown error message: <console>:106: error: type mismatch; found : array[any] required: array[_ <: object] note: >: object, class array invariant in type t. may wish investigate wildcard type such `_ >: object`. (sls 3.2.10) ctor.newinstance(args: _*).asinstanceof[t] ^
i'm new using classtags , understand error because java.lang.object subset of , include non-java objects.
when tried replace anyref (which corresponds java.lang.object in jre), function call results in type mismatch error.
import scala.reflect.classtag def createcaseclass[t](somemap : map[string, anyref])(implicit someclasstag : classtag[t]) = { val ctor = someclasstag.runtimeclass.getconstructors.head val args = someclasstag.runtimeclass.getdeclaredfields.map(x => somemap(x.getname)) ctor.newinstance(args: _*).asinstanceof[t] } val somecc = createcaseclass[somecc](m) name: unknown error message: <console>:106: error: type mismatch; found : scala.collection.immutable.map[string,any] required: map[string,anyref] val somecc = createcaseclass[somecc](m)
what's best way resolve error? suggestions appreciated. thanks!
update 1 - updating implicitly cast anyref leads error 'java.util.nosuchelementexception' on function call.
import scala.reflect.classtag import scala.reflect.runtime.universe._ def createmyclass[t](somemap : map[string, any])(implicit someclasstag : classtag[t]) = { val ctor = someclasstag.runtimeclass.getconstructors.head val args = someclasstag.runtimeclass.getdeclaredfields.map(x => somemap(x.getname)) ctor.newinstance(args.asinstanceof[seq[anyref]]: _*).asinstanceof[t] } val m = map("name" -> "abc", "age" -> 7, "gender" -> "male") case class somecc(name: string, age: int, gender: string) createmyclass[somecc](m) name: java.util.nosuchelementexception message: key not found: $outer stacktrace: @ scala.collection.maplike$class.default(maplike.scala:228) @ scala.collection.abstractmap.default(map.scala:59) @ scala.collection.maplike$class.apply(maplike.scala:141) @ scala.collection.abstractmap.apply(map.scala:59) @ $$$e75186ae1b35495ffea8e318378149a$$$$anonfun$1.apply(<console>:135) @ $$$e75186ae1b35495ffea8e318378149a$$$$anonfun$1.apply(<console>:135) @ scala.collection.traversablelike$$anonfun$map$1.apply(traversablelike.scala:234) @ scala.collection.traversablelike$$anonfun$map$1.apply(traversablelike.scala:234) @ scala.collection.indexedseqoptimized$class.foreach(indexedseqoptimized.scala:33) @ scala.collection.mutable.arrayops$ofref.foreach(arrayops.scala:186) @ scala.collection.traversablelike$class.map(traversablelike.scala:234) @ scala.collection.mutable.arrayops$ofref.map(arrayops.scala:186) @ createmyclass(<console>:135)
what doing wrong here?
use cast:
ctor.newinstance(args.asinstanceof[seq[anyref]]: _*).asinstanceof[t]
note map every field of case class constructor param. incorrect, because case class can have fields aren't in constructor, , code break.
a better idea use scala reflection:
import reflect.runtime.universe._ def mkclassinstance[t: typetag](args: map[string, any]): t = { val rmirror = runtimemirror(getclass.getclassloader) val cmirror = rmirror.reflectclass(typeof[t].typesymbol.asclass) // primary constructor first 1 val ctor = typeof[t].decl(termnames.constructor).asterm.alternatives.head.asmethod val arglist = ctor.paramlists.flatten.map(param => args(param.name.tostring)) cmirror.reflectconstructor(ctor)(arglist: _*).asinstanceof[t] } def mkinnerclassinstance[t: typetag](outer: any)(args: map[string, any]): t = { val rmirror = runtimemirror(getclass.getclassloader) val cmirror = rmirror.reflect(outer).reflectclass(typeof[t].typesymbol.asclass) // primary constructor first 1 val ctor = typeof[t].decl(termnames.constructor).asterm.alternatives.head.asmethod val arglist = ctor.paramlists.flatten.map(param => args(param.name.tostring)) cmirror.reflectconstructor(ctor)(arglist: _*).asinstanceof[t] }
Comments
Post a Comment