将地图转换为嵌套案例 class
Convert a Map to a nested case class
我想将 Map[String, Any]
转换为给定的案例 class,地图可以是嵌套地图。例如,personDataMap
应该转换成Person("evan",24,Address(15213,"5000 Forbes Ave"))
case class Address(zip: Int, name: String)
case class Person(name: String, age: Int, addr: Address)
val addrDataMap = Map("zip" -> 15213, "name" -> "5000 Forbes Ave")
val personDataMap = Map("name" -> "evan", "age" -> 24, "addr" -> addrDataMap)
将 Map[String, Any]
转换为非嵌套大小写 class 的代码可以在下面找到:
import com.twitter.util.{Return, Try}
import scala.reflect._
import scala.reflect.runtime.universe._
// Magic from
// Convert a Map[String, _] to a given case class CC
class CaseClassDataMapConverter[CC <: Product] {
def fromMap(dataMap: Map[String, Any])(implicit tt: TypeTag[CC]): Try[CC] = {
val tClassSymbol = typeOf[CC].typeSymbol.asClass
val rm = typeTag[CC].mirror
val classMirror = rm.reflectClass(tClassSymbol)
val constructor = typeOf[CC].decl(termNames.CONSTRUCTOR).asMethod
val constructorMirror = classMirror.reflectConstructor(constructor)
val constructorTries = constructor.paramLists.flatten.map { param: Symbol =>
val paramName = param.name.toString
val tryResult: Try[Any] =
if (param.typeSignature <:< typeOf[Option[Any]]) {
Return(dataMap.get(paramName))
}
else {
Try(dataMap.getOrElse(paramName, new Exception("Param " + paramName + " not found")))
}
println("paramName = " + paramName + ", result = " + tryResult + ", type = " + param.typeSignature)
tryResult
}
Try.collect(constructorTries).flatMap { constructorArgs =>
Try(constructorMirror(constructorArgs: _*).asInstanceOf[CC])
}
}
}
case class Address(zip: Int, name: String)
case class Person(name: String, age: Int, addr: Address)
val converter = new CaseClassDataMapConverter[Person]()
val addrDataMap = Map("zip" -> 15213, "name" -> "5000 Forbes Ave")
val addrTry = fromMap[Address](addrDataMap) // Return(Address(15213,"5000 Forbes Ave"))
val addr = addrTry.get
val personDataMap = Map("name" -> "evan", "age" -> 24, "addr" -> addr)
val personTry = converter.fromMap(personDataMap) // Return(Person(evan,24,Address(15213,5000 Forbes Ave)))
val person = personTry.get
为了处理嵌套大小写 class,我检查了 param.typeSignature
。如果是大小写 class (Product
),我首先将其值(即嵌套的 Map
)转换为大小写 class。我添加了一个 else-if
分支并更改 personDataMap.addr
以使用 Map
而不是已经制作好的案例 class:
import com.twitter.util.{Return, Try}
import scala.reflect._
import scala.reflect.runtime.universe._
// Magic from
// Convert a Map[String, _] to a given case class CC
class CaseClassDataMapConverter[CC <: Product] {
def fromMap(dataMap: Map[String, Any])(implicit tt: TypeTag[CC]): Try[CC] = {
val tClassSymbol = typeOf[CC].typeSymbol.asClass
val rm = typeTag[CC].mirror
val classMirror = rm.reflectClass(tClassSymbol)
val constructor = typeOf[CC].decl(termNames.CONSTRUCTOR).asMethod
val constructorMirror = classMirror.reflectConstructor(constructor)
val constructorTries = constructor.paramLists.flatten.map { param: Symbol =>
val paramName = param.name.toString
val tryResult: Try[Any] =
if (param.typeSignature <:< typeOf[Option[Any]]) {
Return(dataMap.get(paramName))
}
// Need to deal with: type=Address, result=Map(...)
else if (param.typeSignature <:< typeOf[Product]) {
val nestedDataMap = dataMap.getOrElse(paramName, new Exception("Param " + paramName + " not found"))
val nestedConverter = new CaseClassDataMapConverter[param]()
nestedConverter.fromMap(nestedDataMap)
}
else {
Try(dataMap.getOrElse(paramName, new Exception("Param " + paramName + " not found")))
}
println("paramName = " + paramName + ", result = " + tryResult + ", type = " + param.typeSignature)
tryResult
}
Try.collect(constructorTries).flatMap { constructorArgs =>
Try(constructorMirror(constructorArgs: _*).asInstanceOf[CC])
}
}
}
case class Address(zip: Int, name: String)
case class Person(name: String, age: Int, addr: Address)
val converter = new CaseClassDataMapConverter[Person]()
val addrDataMap = Map("zip" -> 15213, "name" -> "5000 Forbes Ave")
val personDataMap = Map("name" -> "evan", "age" -> 24, "addr" -> addrDataMap)
val personTry = converter.fromMap(personDataMap) // Return(Person(evan,24,Address(15213,5000 Forbes Ave)))
val person = personTry.get
不足为奇,它抱怨:
error: not found: type param
val nestedConverter = new CaseClassDataMapConverter[param]()
我的问题:在这种情况下,是否可以使用类型参数 Address
获得新的 CaseClassDataMapConverter
?如果是这样,该怎么办?谢谢!
仅供参考:运行 环境是 scala 控制台。
您可以去掉类型参数,只需将 TypeTag
作为显式参数传递给 .fromMap
:
class CaseClassDataMapConverter {
private def fromMap(dataMap: Map[String, Any], tt: TypeTag[_]): Try[Any] = {
...
}
}
object CaseClassDataMapConverter {
def fromMap[CC <: Product : TypeTag](dataMap: Map[String, Any]) =
new CaseClassDataMapConverter()
.fromMap(data, typeTag[CC])
.asInstanceOf[Try[CC]]
}
我想将 Map[String, Any]
转换为给定的案例 class,地图可以是嵌套地图。例如,personDataMap
应该转换成Person("evan",24,Address(15213,"5000 Forbes Ave"))
case class Address(zip: Int, name: String)
case class Person(name: String, age: Int, addr: Address)
val addrDataMap = Map("zip" -> 15213, "name" -> "5000 Forbes Ave")
val personDataMap = Map("name" -> "evan", "age" -> 24, "addr" -> addrDataMap)
将 Map[String, Any]
转换为非嵌套大小写 class 的代码可以在下面找到:
import com.twitter.util.{Return, Try}
import scala.reflect._
import scala.reflect.runtime.universe._
// Magic from
// Convert a Map[String, _] to a given case class CC
class CaseClassDataMapConverter[CC <: Product] {
def fromMap(dataMap: Map[String, Any])(implicit tt: TypeTag[CC]): Try[CC] = {
val tClassSymbol = typeOf[CC].typeSymbol.asClass
val rm = typeTag[CC].mirror
val classMirror = rm.reflectClass(tClassSymbol)
val constructor = typeOf[CC].decl(termNames.CONSTRUCTOR).asMethod
val constructorMirror = classMirror.reflectConstructor(constructor)
val constructorTries = constructor.paramLists.flatten.map { param: Symbol =>
val paramName = param.name.toString
val tryResult: Try[Any] =
if (param.typeSignature <:< typeOf[Option[Any]]) {
Return(dataMap.get(paramName))
}
else {
Try(dataMap.getOrElse(paramName, new Exception("Param " + paramName + " not found")))
}
println("paramName = " + paramName + ", result = " + tryResult + ", type = " + param.typeSignature)
tryResult
}
Try.collect(constructorTries).flatMap { constructorArgs =>
Try(constructorMirror(constructorArgs: _*).asInstanceOf[CC])
}
}
}
case class Address(zip: Int, name: String)
case class Person(name: String, age: Int, addr: Address)
val converter = new CaseClassDataMapConverter[Person]()
val addrDataMap = Map("zip" -> 15213, "name" -> "5000 Forbes Ave")
val addrTry = fromMap[Address](addrDataMap) // Return(Address(15213,"5000 Forbes Ave"))
val addr = addrTry.get
val personDataMap = Map("name" -> "evan", "age" -> 24, "addr" -> addr)
val personTry = converter.fromMap(personDataMap) // Return(Person(evan,24,Address(15213,5000 Forbes Ave)))
val person = personTry.get
为了处理嵌套大小写 class,我检查了 param.typeSignature
。如果是大小写 class (Product
),我首先将其值(即嵌套的 Map
)转换为大小写 class。我添加了一个 else-if
分支并更改 personDataMap.addr
以使用 Map
而不是已经制作好的案例 class:
import com.twitter.util.{Return, Try}
import scala.reflect._
import scala.reflect.runtime.universe._
// Magic from
// Convert a Map[String, _] to a given case class CC
class CaseClassDataMapConverter[CC <: Product] {
def fromMap(dataMap: Map[String, Any])(implicit tt: TypeTag[CC]): Try[CC] = {
val tClassSymbol = typeOf[CC].typeSymbol.asClass
val rm = typeTag[CC].mirror
val classMirror = rm.reflectClass(tClassSymbol)
val constructor = typeOf[CC].decl(termNames.CONSTRUCTOR).asMethod
val constructorMirror = classMirror.reflectConstructor(constructor)
val constructorTries = constructor.paramLists.flatten.map { param: Symbol =>
val paramName = param.name.toString
val tryResult: Try[Any] =
if (param.typeSignature <:< typeOf[Option[Any]]) {
Return(dataMap.get(paramName))
}
// Need to deal with: type=Address, result=Map(...)
else if (param.typeSignature <:< typeOf[Product]) {
val nestedDataMap = dataMap.getOrElse(paramName, new Exception("Param " + paramName + " not found"))
val nestedConverter = new CaseClassDataMapConverter[param]()
nestedConverter.fromMap(nestedDataMap)
}
else {
Try(dataMap.getOrElse(paramName, new Exception("Param " + paramName + " not found")))
}
println("paramName = " + paramName + ", result = " + tryResult + ", type = " + param.typeSignature)
tryResult
}
Try.collect(constructorTries).flatMap { constructorArgs =>
Try(constructorMirror(constructorArgs: _*).asInstanceOf[CC])
}
}
}
case class Address(zip: Int, name: String)
case class Person(name: String, age: Int, addr: Address)
val converter = new CaseClassDataMapConverter[Person]()
val addrDataMap = Map("zip" -> 15213, "name" -> "5000 Forbes Ave")
val personDataMap = Map("name" -> "evan", "age" -> 24, "addr" -> addrDataMap)
val personTry = converter.fromMap(personDataMap) // Return(Person(evan,24,Address(15213,5000 Forbes Ave)))
val person = personTry.get
不足为奇,它抱怨:
error: not found: type param
val nestedConverter = new CaseClassDataMapConverter[param]()
我的问题:在这种情况下,是否可以使用类型参数 Address
获得新的 CaseClassDataMapConverter
?如果是这样,该怎么办?谢谢!
仅供参考:运行 环境是 scala 控制台。
您可以去掉类型参数,只需将 TypeTag
作为显式参数传递给 .fromMap
:
class CaseClassDataMapConverter {
private def fromMap(dataMap: Map[String, Any], tt: TypeTag[_]): Try[Any] = {
...
}
}
object CaseClassDataMapConverter {
def fromMap[CC <: Product : TypeTag](dataMap: Map[String, Any]) =
new CaseClassDataMapConverter()
.fromMap(data, typeTag[CC])
.asInstanceOf[Try[CC]]
}