从字符串termname获取类型

时间:2018-02-21 19:06:07

标签: scala reflection types casting type-conversion

基本上我想将非类型化的JSON读入由字符串指定的类型。伪代码。

def getObject(json: Json, typeString: String): typeOf(typeString) = extract[typeOf(typeString)](json)

typeOf只是一些从字符串中提供类型的随机事物。

1 个答案:

答案 0 :(得分:0)

我会说没有运行时反射是不可能的。使用运行时反射,我会尝试以下方法:

  • Class[_]获取ClassLoader的名称 - 仅当您在String中指定了全名(包含所有内容)
  • 时,它才有效
  • 然后使用类似Jackson的东西,它使用运行时反射来(de)序列化事物new ObjectMapper().readValue(json, obtainedClass)
  • 显然返回类型是Any - 理论上你可以在这里使用path-dependent types,但我个人认为收效甚微。

然而:

  • 它会非常脆弱 - JSON和类之间的任何不匹配都会失败,
  • 你必须传递一个类的全名(再次,脆弱) - 如果你从外部传递数据,这听起来像一个潜在的安全问题,如果你在内部传递数据...为什么不使用类型类?
  • 如果你需要持久性...那么你可以将它与一个鉴别器一起保存,你可以使用它来提供正确的类型类。 (在序列化的所有类都是有限的并且可以很容易地跟踪之后)。然后输入安全方法,例如Circe是可能的。

嗯,我想你也可以将签名改为:

def getObject[T: ClassTag](json: Json): T

def getObject[T](json: Json, clazz: Class[T]): T

并确保该函数返回您想要的内容。通过名称获取类[_]并传递它会使我们减少原始解决方案。

编辑:

显示如何从鉴别器(伪代码)中提取类型的示例:

// store discriminator in DB
// use it to deserialize and dispatch with predefined function
def deserializeAndHandle(discriminator: String, json: String): Unit = discriminator match {
  case "my.package.A" => decode[my.package.A](json).map(handlerForA)
  case "my.package.B" => decode[my.package.B](json).map(handlerForB)
  case "my.package.C" => decode[my.package.C](json).map(handlerForC)
  case _              => 
}

deserializeAndHandle(discriminator, json)

// store discriminator in DB
// use it to deserialize to Object which can be pattern-matched later
def deserializeToObject(discriminator: String, json: String): Option[Any] = discriminator match {
  case "my.package.A" => decode[my.package.A](json).toOption
  case "my.package.B" => decode[my.package.B](json).toOption
  case "my.package.C" => decode[my.package.C](json).toOption
  case _              => None
}

deserializeToObject(discriminator, json) map {
  case a : A => ...
  case b : B => ...
  case c : C => ...
} getOrElse ???

// wrap unrelated types with sealed trait to make it sum type
// use sum type support of Circe
sealed trait Envelope
final case class EnvelopeA(a: A) extends Envelope
final case class EnvelopeB(b: B) extends Envelope
final case class EnvelopeA(c: C) extends Envelope
def deserializeEnveloped(json): Option[Envelope] = decode[Envelope](json).toOption

deserializeEnveloped(json) map {
  case EnvelopeA(a) => ...
  case EnvelopeB(b) => ...
  case EnvelopeC(c) => ...
} getOrElse ???