使用Scala泛型我试图在Play应用程序中抽象一些常用函数。函数返回Seq
s,其中包含从REST JSON服务反序列化的对象。
def getPeople(cityName: String): Future[Seq[People]] = {
getByEndpoint[People](s"http://localhost/person/$cityName")
}
def getPeople(): Future[Seq[Dog]] = {
getByEndpoint[Dog]("http://localhost/doge")
}
使用泛型将获取和反序列化逻辑打包到单个函数中。
private def getByEndpoint[T](endpoint: String): Future[Seq[T]] = {
ws.url(endpoint)
.get()
.map(rsp => rsp.json)
.flatMap { json =>
json.validate[Seq[T]] match {
case s: JsSuccess[Seq[T]] =>
Future.successful(s.get)
case e: JsError =>
Future.failed(new RuntimeException(s"Get by endpoint JSON match failed: $e"))
}
}
}
问题是我得到了“No Json deserializer found for type Seq[T]. Try to implement an implicit Reads or Format for this type.
”。我确定我没有在T
中正确使用Seq[T]
(至少根据我的C#/ Java存储器),但我找不到任何线索如何在Scala中正确使用它。一切都按预期工作,不使用泛型。
答案 0 :(得分:2)
播放JSON使用type classes来捕获有关哪些类型可以(取消)与JSON序列化以及如何进行序列化的信息。如果您在范围内具有类型为Format[Foo]
的隐式值,则称其为Format
的{{1}}类型类的实例。
这种方法的优点在于它为我们提供了一种约束泛型类型(并在编译时检查这些约束)的方法,这种方法并不依赖于子类型。例如,标准库Foo
无法扩展Play(或任何其他库)可能提供的某种String
特性,因此我们需要某种方式说"我们知道如何将Jsonable
编码为JSON"这并不会使String
成为我们自己定义的某种特质的子类型。
在Play JSON中,您可以通过定义隐式String
实例来实现此目的,而Play本身为您提供了许多这些实例(例如,如果您为Format
提供了一个,那么&#39 ;给你一个T
)。 Seq[T]
上的validate
方法需要其中一个实例(实际上是JsValue
,Format
的子类型,但这里的类型参数不是非常相关) - 在这种情况下为Reads
- 除非编译器能够找到该实例,否则它不会编译。
您可以通过将约束添加到您自己的通用方法来提供此实例:
Seq[T]
现在使用private def getByEndpoint[T: Format](endpoint: String): Future[Seq[T]] = {
...
}
语法,您已指定T: Format
必须有Format
个实例(即使您没有约束T
以任何其他方式),因此编译器知道如何为T
调用所需的Format
提供Seq[T]
实例。