簡體   English   中英

沒有適用於案例類別Type的TypeTag

[英]No TypeTag available for case class Type

我想生成一個將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中要做的是:

  1. 從輸入中提取Map[String, _]
  2. 從輸入中提取Map[String, Types]
  3. 將字段name的值從Name轉換為Map[String, _]
  4. 還原第3步以取回Name類型的Object

這就是我試圖處理這種新情況的方式:

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

我不確定我是否正確解釋了您的問題,但是據我了解,這可以很好地解決並且可以通過無形安全鍵入。 首先,您需要將Document轉換為Map shapeless可以在該類型類的一個為你做這個現成的ops文件夾中。 如果我們將其捆綁為一個函數,並使用某種機器將所有內容組合在一起,我們將得到類似以下內容的信息:

import shapeless._

def ObjectToMap[A, Repr <: HList](obj: A)(
  implicit
  gen: LabelledGeneric.Aux[A,Repr], //Convert to generic representation
  toMap: ops.record.ToMap[Repr] //Convert generic representation to Map[Symbol,_]
) = toMap(gen.to(obj))

哪個輸出

val m = ObjectToMap(myDoc)
println(m) //Map('code -> ABCD, 'name -> Name(Roger,Rabbit), 'idx -> 12)

往另一個方向走比較復雜。 存在一個ops.maps.FromMap類型類。 但是,我們希望能夠指定type參數,然后讓編譯器仍然驗證泛型表示形式為HList ,以與FromMap的簽名匹配。 由於從屬類型不能與在同一參數列表中定義的其他變量一起使用,並且我們只能得到一個隱式參數列表,因此我們需要采取一些技巧來控制類型參數:

trait MapToObject[A]{
  def from[Repr <: HList](m: Map[_,_])(
    implicit
    gen: LabelledGeneric.Aux[A,Repr],
    fromMap: ops.maps.FromMap[Repr]
  ): Option[A] = fromMap(m).map(gen.from)
}

object MapToObject{
  def apply[A](
    implicit gen: LabelledGeneric[A]
  ): MapToObject[A] = new MapToObject[A]{}
}

當我們運行前一個塊的輸出時,我們得到:

val doc = MapToObject[Documents].from(m)
println(doc) //Some(Documents(12,Name(Roger,Rabbit),ABCD))

type nameT = nameType.type不正確。 nameType.type是此特定變量nameType (單例)類型,並且您需要name字段的類型。 這是偶然的,因為實際上您不在fromMap2使用TruntimeMirror(classTag[T].runtimeClass.getClassLoader)可以在那里替換為currentMirror )。

你想叫你的原始fromMapfromMapToObject 你知道universe.Typename和它足以找到TypeTagClassTag隱參數fromMap 但這還不足以找到T 事實是,由於您使用運行時反射, ClassTag在運行時知道universe.Type (以及TypeTagClassTag )。 但是要調用fromMap您需要在編譯時知道T 因此,一種方法是使用編譯時反射,即宏。 另一種方法是避免T並像您一樣使用值參數。

要在Scala中映射的案例類

Scala:將地圖轉換為案例類

我找到了解決方案。 由於我無法獲取classTagtypeTag將函數toMap修改如下:

def fromMap2[T : ClassTag ](m: Map[String,_], mSymbol: Symbol, mType :Type): Any = {
    val rm = runtimeMirror(classTag[T].runtimeClass.getClassLoader)
    val classTest = mSymbol.asClass
    val classMirror = rm.reflectClass(classTest)
    val constructor = mType.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]

  }

因此,現在我需要傳遞T的類型和符號。我可以得到這兩個值,如下所示:

//Converting an Object into a Map
val r = currentMirror.reflect(input)
val mapValues = 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 mapTypes = 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

val mapTypesSymbols = 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.typeSymbol)
  .toMap

val nameType = mapTypes("name")
val nameTypeSymbols =  mapTypesSymbols("name")
val nameValue =  mapValues("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 = fromMap2[nameT](nameAsMapValues, nameTypeSymbols, nameType)

即使這有效,我相信這也是非常反模式。 因此,如果有人可以指出改進方法,我將保留這個問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM