【发布时间】:2019-06-30 12:15:20
【问题描述】:
我想生成一个方法,它将Object 转换为Map[String, _],然后再从Map[String, _] 转换为Object。
我生成初始对象如下:
case class Name (firstName : String, lastName : String)
case class Documents (idx: String, name: Name, code: String)
val mName1 = Name("Roger", "Rabbit")
val myDoc = Documents("12", mName1, "ABCD")
然后以下方法将给定的Map[String, _] 转换为Object:
def fromMap[T : TypeTag: ClassTag ](m: Map[String,_]) = {
val rm = runtimeMirror(classTag[T].runtimeClass.getClassLoader)
val classTest = typeOf[T].typeSymbol.asClass
val classMirror = rm.reflectClass(classTest)
val constructor = typeOf[T].decl(termNames.CONSTRUCTOR).asMethod
val constructorMirror = classMirror.reflectConstructor(constructor)
val constructorArgs = constructor.paramLists.flatten.map( (param: Symbol) => {
val paramName = param.name.toString
if(param.typeSignature <:< typeOf[Option[Any]])
m.get(paramName)
else
m.get(paramName).getOrElse(throw new IllegalArgumentException("Map is missing required parameter named " + paramName))
})
constructorMirror(constructorArgs:_*).asInstanceOf[T]
}
在以下方法中,我将初始的Object 转换为Map[String, _],然后返回Object(通过调用上述方法):
def fromMapToObject(input: Any) : Unit= {
println("input: "+input)
//Converting an Object into a Map
val r = currentMirror.reflect(input)
val docAsMapValues = r.symbol.typeSignature.members.toStream
.collect{case s : TermSymbol if !s.isMethod => r.reflectField(s)}
.map(r => r.symbol.name.toString.trim -> r.get)
.toMap
println("intermediate: "+docAsMapValues)
val obj = fromMap[Documents](docAsMapValues)
println("output: "+obj)
}
所以如果我打电话:
fromMapToObject(myDoc)
输入和输出将匹配。
问题,想更进一步,我现在想对name 字段做同样的事情,它的类型是Name。但是我希望这一步是通用的,因为在不知道字段name 的类型的情况下,我可以将其转换为Map[String, _],并从Map[String, _] 转换回Object。
所以我现在在 fromMapToObject 中要做的是:
- 从输入中提取
Map[String, _] - 从输入中提取
Map[String, Types] - 将字段
name的值从Name转换为Map[String, _] - 还原第 3 步以取回
Name类型的对象
这就是我尝试处理这个新场景的方式:
def fromMapToObject[T: TypeTag: ClassTag](input: Any) : Unit = {
println("input: "+input)
//Converting an Object into a Map
val r = currentMirror.reflect(input)
val docAsMapValues = r.symbol.typeSignature.members.toStream
.collect{case s : TermSymbol if !s.isMethod => r.reflectField(s)}
.map(r => r.symbol.name.toString.trim -> r.get)
.toMap
val docAsMapTypes = r.symbol.typeSignature.members.toStream
.collect{case s : TermSymbol if !s.isMethod => r.reflectField(s)}
.map(r => r.symbol.name.toString.trim -> r.symbol.typeSignature)
.toMap
// Here I extract from the map the value and type of the attribute name
val nameType = docAsMapValues("name")
val nameValue = docAsMapValues("name")
// Converting Name into a map
val r2 = currentMirror.reflect(nameValue)
val nameAsMapValues = r2.symbol.typeSignature.members.toStream
.collect{case s : TermSymbol if !s.isMethod => r2.reflectField(s)}
.map(r2 => r2.symbol.name.toString.trim -> r2.get)
.toMap
type nameT = nameType.type
val obj = fromMap[nameT](nameAsMapValues)
}
但是在 Intellij 中编译时出现以下错误:
Error:(111, 29) No TypeTag available for nameT
val obj = fromMap[nameT](nameAsMapValues)
我想知道如何将从r.symbol.typeSignature 返回的runtime.universe.Type 转换为TypeTag : ClassTag
【问题讨论】:
-
这听起来像 Shapeless 可以解决 (但我对 Shapeless 的了解并不多,无法确定)。另外,
Map[String, _]看起来有点类似于 JSON 或 HOCON,您是否尝试过搜索已经完成这项工作的库?喜欢 Circe 或 Spary? -
@LuisMiguelMejíaSuárez 谢谢,我会看看你提到的图书馆。
-
Circe 实际上使用 shapeless 作为它的自动推导,所以你肯定是在正确的轨道上!我没有足够的时间浏览所有内容,所以我不完全确定您的意图是什么,但我建议您查看 Typeclasses 和可能的 Shapeless。您能否添加一个鼓舞人心的示例,您希望该功能的最终使用情况是否一切正常?
-
@Ethan 所以动机基本上是这样的。我得到一个对象,我对对象的结构一无所知(不知道属性的名称,它的类型,如果它们是嵌套的......)。但是他们会给我一条通往我需要修改的一个或多个属性的路径,以及到达该属性的路径。例如:更改为大写 Documents.name.firstName。所以我需要访问具有此属性作为变量的对象中的属性(这就是我转换为映射的原因),然后一旦我对目标应用操作,我应该得到一个对象。
-
当你说你有一个对象时,你的意思是
java.lang.Object还是其他类似 Json 对象的东西?
标签: scala reflection scala-reflect