scala - 带有内部案例类的案例类的字符串列表

标签 scala shapeless

假设我有 2 个案例类:

case class Money(amount: Int, currency: String)
case class Human(name: String, money: Money)

有没有一种很好的方法可以将字符串列表“翻译”为 Human 类?喜欢:
def superMethod[A](params: List[String]): A = ???

val params: List[Any] = List("john", 100, "dollar")
superMethod(params) // => Human("john", Money(100, "dollar"))

所以基本上我只在运行时知道类型 A

更新 : 我找到了~我要找的东西。看来我可以通过无形来做到这一点。 example我在github上找到的

最佳答案

这是一个适用于泛型类的实现 A .
它依赖于运行时反射(即,可以在运行时将不同的 TypeTag 传递给该方法)。要使用此方法,必须满足以下明显条件:

  • A必须在类路径上,否则可以被使用的类加载器加载
  • TypeTag必须可用于 A在调用站点。

  • 实际实现在Deserializer目的。然后是一个小演示。
    解串器:
    import scala.reflect.runtime.universe.{TypeTag, Type}
    
    object Deserializer {
    
      /** Extracts an instance of type `A` from the 
        * flattened `Any` constructor arguments, and returns 
        * the constructed instance together with the remaining
        * unused arguments.
        */
      private def deserializeRecHelper(
        flattened: List[Any], 
        tpe: Type
      ): (Any, List[Any]) = {
        import scala.reflect.runtime.{universe => ru}
        
        // println("Trying to deserialize " + tpe + " from " + flattened)
    
        // println("Constructor alternatives: ")
        // val constructorAlternatives = tpe.
        //   member(ru.termNames.CONSTRUCTOR).
        //   asTerm.
        //   alternatives.foreach(println)
    
        val consSymb = tpe.
          member(ru.termNames.CONSTRUCTOR).
          asTerm.
          alternatives(0).
          asMethod
    
        val argsTypes: List[Type] = consSymb.paramLists(0).map(_.typeSignature)
        if (tpe =:= ru.typeOf[String] || argsTypes.isEmpty) {
          val h :: t = flattened
          (h, t)
        } else {
          val args_rems: List[(Any, List[Any])] = argsTypes.scanLeft(
            (("throwaway-sentinel-in-deserializeRecHelper": Any), flattened)
          ) { 
            case ((_, remFs), t) => 
            deserializeRecHelper(remFs, t)
          }.tail
      
          val remaining: List[Any] = args_rems.last._2
          val args: List[Any] = args_rems.unzip._1
      
          val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)
          val classMirror = runtimeMirror.reflectClass(tpe.typeSymbol.asClass)
          val cons = classMirror.reflectConstructor(consSymb)
      
          // println("Build constructor arguments array for " + tpe + " : " + args)
    
          val obj = cons.apply(args:_*)
          (obj, remaining)
        }
      }
    
      def deserialize[A: TypeTag](flattened: List[Any]): A = {
        val (a, rem) = deserializeRecHelper(
          flattened, 
          (implicitly: TypeTag[A]).tpe
        )
    
        require(
          rem.isEmpty, 
          "Superfluous arguments remained after deserialization: " + rem
        )
    
        a.asInstanceOf[A]
      }
    }
    
    演示:
    case class Person(id: String, money: Money, pet: Pet, lifeMotto: String)
    case class Money(num: Int, currency: String)
    case class Pet(color: String, species: Species)
    case class Species(description: String, name: String)
    
    object Example {
      def main(args: Array[String]): Unit = {
        val data = List("Bob", 42, "USD", "pink", "invisible", "unicorn", "what's going on ey?")
        val p = Deserializer.deserialize[Person](data)
        println(p)
      }
    }
    
    输出:
    Person(Bob,Money(42,USD),Pet(pink,Species(invisible,unicorn)),what's going on ey?)
    

    讨论
    此实现不限于 case 类,但它要求每个“类树节点”类都有一个构造函数,该构造函数接受
  • 原始类型(Int、Float)或
  • 字符串,或
  • 其他“类树节点”类。

  • 请注意,该任务有点不合适:说所有构造函数参数都扁平化在一个列表中是什么意思?鉴于类(class) Person(name: String, age: Int) ,将 List[Any]包含 name 的每个字节作为单独的条目?可能不是。因此,字符串由反序列化器以特殊方式处理,出于同样的原因,不支持所有其他类似集合的实体(不清楚在哪里停止解析,因为集合的大小未知)。

    关于scala - 带有内部案例类的案例类的字符串列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49056488/

    相关文章:

    scala - 如何使用 shapeless 在 Scala 列表中拆分

    scala - 包含一些 F[_] 的元组/hlist 上的通用变换/折叠/映射

    scala - 我可以从模板(各种)生成 Scala 代码吗?

    java - Play 推荐的 mongo db 插件是什么!框架?

    scala - 无形:IsHCons,未找到隐式

    scala - HList参数为HNil时如何对Json进行编码?

    scala - 编译时两个大小相同的 HList 的总和?

    scala - 找不到 GeneratorDrivenPropertyChecks 特性

    scala - 如何过滤相邻顶点类型的混合节点图

    scala - 使用 Spark 将 csv.gz 文件转换为 Parquet