对于任何可以返回多个类的API,但在类型为Any
的集合中,会出现此问题。
一个具体的例子是使用内置的JSON解析器(scala.util.parsing.json
)处理JSON:返回的值是Map[String,Any]
,因为每个JSON键值对中的值可以是任何JSON类型。
从这些嵌套的Map
中提取值似乎需要进行类型测试和转换,这相当丑陋。特别是,我们最终得到了多个与返回类型相同的函数(例如String,Double,Map ...),用于检查和转换。
是否可以抽象出这种类型,以便只需要一个通用的get[T](...): T
函数,避免使用这个样板?
我一直在关注TypeTag
,但到目前为止我找到的所有示例都是关于参数类型的抽象,而不是返回类型。
澄清:我知道还有很多其他JSON解析器提供了更好的接口和模式匹配等,但我只是对这个重构的一般问题感兴趣,因为处理返回Any
。
import scala.util.parsing.json._
object ParseJSON {
val text = """{"str":"A String", "num":123, "obj": { "inner":"value" }}"""
val json = JSON.parseFull(text).get.asInstanceOf[Map[String,Any]]
//> ... Map(str -> A String, num -> 123.0, obj -> Map(inner -> value))
// Three essentially identical functions:
def getString(m:Map[String,Any], k:String): Option[String] = {
m.get(k).flatMap{ v =>
if (v.isInstanceOf[String]) Some(v.asInstanceOf[String]) else None
}
}
def getDouble(m:Map[String,Any], k:String): Option[Double] = {
m.get(k).flatMap{ v =>
if (v.isInstanceOf[Double]) Some(v.asInstanceOf[Double]) else None
}
}
def getObject(m:Map[String,Any], k:String): Option[Map[String, Any]] = {
m.get(k).flatMap{ v =>
if (v.isInstanceOf[Map[_,_]]) Some(v.asInstanceOf[Map[String,Any]])
else None
}
}
getString(json, "str") //> res0: Option[String] = Some(A String)
getString(json, "num") //> res1: Option[String] = None
getObject(json, "obj")
//> res3: Option[Map[String,Any]] = Some(Map(inner -> value))
}
我最初认为这可以通过通用类来解决:
class Getter[T] {
def get(m: Map[String, Any], k: String): Option[T] = {
m.get(k).flatMap { v =>
if (v.isInstanceOf[T]) Some(v.asInstanceOf[T]) else None
}
}
}
new Getter[String].get(json, "str")
但正如Oleg Pyzhcov指出的那样(在我现在删除的答案中),类型擦除会阻止它在运行时检测类型是否正确。
答案 0 :(得分:1)
对失败尝试的修复非常简单:
import scala.reflect.ClassTag
class Getter[T: ClassTag] {
def get(m: Map[String, Any], k: String): Option[T] = {
m.get(k).flatMap {
case v: T => Some(v)
case _ => None
}
}
}
new Getter[String].get(json, "str")
在: T
可用时,特别针对ClassTag[T]
进行模式匹配。
不幸的是,如果您希望T
本身是通用类型,请键入删除回击:Getter[List[String]]
只能检查它是否通过List
,而不是其类型参数。
答案 1 :(得分:0)
你可能想要使用JSON库,我个人建议Circe。它可以平滑地处理类型安全的序列化和反序列化,并且可以轻松扩展。如果您正在寻找多态反序列化,则需要将类型存储在JSON中,而Circe通常也会通过支持密集的案例类层次结构来处理这些类型。