如何解决使用受控类型转换将 Map[String, Any] 转换为嵌套 case 类
我想将 Map[String,Any] 转换为给定的 case 类,并且该映射可以是嵌套映射。 case 类可以有某些字段作为 Optional,如果地图没有相关字段,它将有一个默认值。 例如。
case class CC(a: Int,b: Option[Int],c: String)
Map(a -> 1,c -> "abc")
result case class : CC(1,None,"abc")
唯一需要注意的是我需要控制这些字段的 typeSignatures。 例如:
Map(a ->,b -> "10",c ->"abc"). \\Note b is string here
result case class : CC(1,Some(10),"abc")
我得到了一些可以执行这种确切行为的代码(下面的代码),但我似乎无法使其适用于嵌套案例类
import scala.reflect.runtime.universe._
object DynamicclassFactory {
val classLoaderMirror = runtimeMirror(getClass.getClassLoader)
}
class DynamicclassFactory[T: TypeTag] {
import DynamicclassFactory.classLoaderMirror
val tpe = typeOf[T]
val classSymbol = tpe.typeSymbol.asClass
val companion = tpe.typeSymbol.companion
val classMirror = classLoaderMirror reflectClass classSymbol
val instanceMirror = classLoaderMirror.reflectModule(companion.asModule)
val objMirror = classLoaderMirror.reflect(instanceMirror.instance)
val constructorSymbol = tpe.decl(termNames.CONSTRUCTOR)
val constructorArgs = constructorSymbol.asMethod.paramLists.flatten.map{
p => (p.name.decodedname.toString,p.typeSignature)
}
val defaultConstructor =
if (constructorSymbol.isMethod) constructorSymbol.asMethod
else {
val ctors = constructorSymbol.asTerm.alternatives
ctors.map { _.asMethod }.find { _.isPrimaryConstructor }.get
}
val constructorMethod = classMirror reflectConstructor defaultConstructor
def buildParameters: List[String] = constructorArgs.map ( x => x._1 )
def getCompanionParam(name: String): Any =
if(classSymbol.isCaseClass) {
objMirror.reflectMethod(instanceMirror.symbol.info.decl(TermName(name)).asMethod)("test")
}
def getDefaults = {
val defaults = companion.typeSignature.members.filter { m => m.isMethod && m.name.toString.contains("apply$default")}.toList.map { x => (x.asMethod.name.toTermName.toString.replaceAll("[^0-9]","").toInt-1,getCompanionParam(x.asMethod.name.toTermName.toString)) }.toMap
val defaultValues = scala.collection.mutable.Map[String,Any]()
for( (x,i) <- buildParameters.view.zipwithIndex ) {
if(defaults.contains(i)) {
defaultValues(x) = defaults(i)
}
}
defaultValues.toMap
}
def buildWith(args: Seq[_]): T = {
constructorMethod(args: _*).asInstanceOf[T]
}
def buildSafe(configuration: Map[String,Any]): T = {
val config = getDefaults ++ configuration
val safeConfig = safeTypeConvert(config,constructorArgs.toMap)
buildWith(constructorArgs.map { x => safeConfig(x._1) })
}
def safeTypeConvert(v: Map[String,Any],t: Map[String,Type]): Map[String,Any] = {
val o = scala.collection.mutable.Map[String,Any]()
val z = v.filter{
case (k,v) =>
t.contains(k)
}
z.foreach {
case (k,v) =>
val typeS: Type = t(k)
try {
if (typeS <:< typeOf[Option[String]]) {
v match {
case x: Int => o(k) = Some(x.toString)
case y: Option[nothing] => o(k) = None
case _ => o(k) = Some(v.asInstanceOf[String])
}
}
else if (typeS <:< typeOf[Option[Int]]) {
v match {
case x: String => o(k) = Some(x.toFloat.toInt)
case y: Option[nothing] => o(k) = None
case _ => o(k) = Some(v.asInstanceOf[Int])
}
}
else if (typeS <:< typeOf[Option[Boolean]]) {
v match {
case x: String => o(k) = Some(x.toLowerCase.toBoolean)
case y: Option[nothing] => o(k) = None
case _ => o(k) = v
}
}
else if (typeS <:< typeOf[Int]) {
v match {
case x: String => o(k) = x.toFloat.toInt // this is to handle decimals returned from json
case _ => o(k) = v.asInstanceOf[Int]
}
} else if (typeS =:= typeOf[String]) {
v match {
case x: Int => o(k) = x.toString
case _ => o(k) = v.asInstanceOf[String]
}
} else if (typeS =:= typeOf[Boolean]) {
v match {
case x: String => o(k) = x.toLowerCase.toBoolean
case _ => o(k) = v
}
}
else if (typeS <:< typeOf[List[_]]) {
if (v == Nil) {
o(k) = List[String]()
} else {
o(k) = v.asInstanceOf[List[_]]
}
} else if (typeS <:< typeOf[Map[_,_]]) {
if (v == None) {
o(k) = Map()
} else o(k) = v
}
else {
throw new Exception(s"sdf $k must be of type ${typeS.typeSymbol.name.decoded},got ${v.getClass.getName}: ${v.toString}")
}
} catch {
case e@(_: ClassCastException | _: NumberFormatException | _: IllegalArgumentException) =>
throw new Exception(s"$k must be of type ${typeS.typeSymbol.name.decoded},got ${v.getClass.getName}: ${v.toString}")
}
}
return o.toMap
}
}
以上对于不是嵌套地图的地图非常有效,但对于嵌套地图失败
case class Address(addLine1: String,addLine2: Optional[String],zip: Long)
case class Inner(id: Option[String],addr: Option[Address],isPath: Option[Boolean])
case class Outer(name: String,addtlnInfo: Inner)
val map: Map[String,Any] = Map("name" -> "john","addtlnInfo" -> Map("id" -> 123,"addr" -> Map("addLine1" -> "901 Lincoln St","zip" -> "80101")))
val ins = new DynamicclassFactory[Outer]
ins.buildSafe(map)
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。