【发布时间】:2017-09-26 10:21:25
【问题描述】:
任何可以返回多个类但在 Any 类型的集合中的 API 都会出现此问题。
一个具体的例子是使用内置的 JSON 解析器 (scala.util.parsing.json) 处理 JSON:返回的值是 Map[String,Any],因为每个 JSON 键值对中的值可以是任何 JSON 类型。
从这些嵌套的Maps 中提取值似乎需要类型测试和强制转换,这相当难看。特别是,我们最终得到了多个相同的函数,除了用于检查和强制转换的返回类型(例如 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 指出的(在我现在已删除的答案中),类型擦除会阻止它在运行时检测类型是否正确。
【问题讨论】:
-
不再推荐包
scala.util.parsing.json。而是使用 JSON 专用库 -
同意 - 但正如我在问题中所说,我对如何解决这个重构问题很感兴趣(以 JSON 解析为例)
-
修复一个不鼓励的库的限制几乎没有价值
标签: scala