Play 2.0 中文资料 - Play JSON 库使用泛型



概述

当使用基于 JSON 库的 typeclass(Unmi: typeclass 还没摸准翻译成什么词较合适,此前译作 类型类,觉得有点不妥,所以暂时保留原样) 时,可能会把泛型支持包含进这些 typeclass 中来. 针对基于终端控制查询参数,使用基本的结构作为查询结果的 REST API 来说可能是一个很好的应用方式.

Scala 对泛型的支持

给定如下基本的结构作为搜索结果:
1case class SearchResults[T](
2    elements: List[T], 
3    page: Int, 
4    pageSize: Int, 
5    total :Int
6)

Unmi 注: 上面  case class 涉及到了 Scala 的样本类的特性,Scala 会给这个类自动添加一些句法:1)添加与类名一致的工厂方法,2)参数列表中的所有参数前隐式获得了 val 前缀,即会由相应的的实例变量保持状态,3)自动添加了 toString, hashCode, 和  equals 方法。

现在你必须确保读写方法能够处理泛型,它以为你声明了隐式的参数用于支持类型 T 的读写.
 1object SearchResults
 2{
 3  implicit def searchResultsReads[T](implicit fmt: Reads[T]): Reads[SearchResults[T]] = new Reads[SearchResults[T]] {
 4    def reads(json: JsValue): SearchResults[T] = new SearchResults[T] (
 5
 6     (json \ "elements") match {
 7        case JsArray(ts) => ts.map(t => fromJson(t)(fmt))
 8        case _ => throw new RuntimeException("Elements MUST be a list")
 9      },
10      (json \ "page").as[Int],
11      (json \ "pageSize").as[Int],
12      (json \ "total").as[Int]
13    )
14  }
15
16  implicit def searchResultsWrites[T](implicit fmt: Writes[T]): Writes[SearchResults[T]] = new Writes[SearchResults[T]] {
17    def writes(ts: SearchResults[T]) = JsObject(Seq(
18        "page" -> JsNumber(ts.page),
19        "pageSize" -> JsNumber(ts.pageSize),
20        "total" -> JsNumber(ts.total),
21        "elements" -> JsArray(ts.elements.map(toJson(_)))
22    ))  
23  }
24}

Unmi 注: 这应该是比较高阶的应用了,一般不用太关注这里的细节。而且上面的代码我运行时在行 case JsArray(ts) => ts.map(t => fromJson(t)(fmt)) 上会报错误 “type mismatch; found: Seq[T] required: List[T]”,需把 elements 类型改为  Seq[T]。可运行的完整的 SearchResults.scala 如下:
 1import play.api.libs.json._
 2import play.api.libs.json.Json._
 3
 4case class SearchResults[T](
 5    elements: Seq[T],
 6    page: Int,
 7    pageSize: Int,
 8    total :Int
 9)
10
11object SearchResults
12{
13  implicit def searchResultsReads[T](implicit fmt: Reads[T]): Reads[SearchResults[T]] = new Reads[SearchResults[T]] {
14    def reads(json: JsValue): SearchResults[T] = new SearchResults[T] (
15
16     (json \ "elements") match {
17          case JsArray(ts) => ts.map(t => fromJson(t)(fmt))
18          case _ => throw new RuntimeException("Elements MUST be a list")
19      },
20      (json \ "page").as[Int],
21      (json \ "pageSize").as[Int],
22      (json \ "total").as[Int]
23    )
24  }
25
26  implicit def searchResultsWrites[T](implicit fmt: Writes[T]): Writes[SearchResults[T]] = new Writes[SearchResults[T]] {
27    def writes(ts: SearchResults[T]) = JsObject(Seq(
28        "page" -> JsNumber(ts.page),
29        "pageSize" -> JsNumber(ts.pageSize),
30        "total" -> JsNumber(ts.total),
31        "elements" -> JsArray(ts.elements.map(toJson(_)))
32    ))  
33  }
34}

基本的 JSON 数据类型

给定上面的类型类, 你可以轻易的创建和使用作何与 play.api.libs.json 包中基本数据类型一致的类型
1val input = """{"page": 1,"pageSize":2, "total": 3, "elements" : [1, 2, 3]}"""
2val ret = play.api.libs.json.Json.parse(input).as[SearchResults[Int]]

Unmi 注: 在 Controller 中用  Ok(toJson(ret)) 就能输出 JSON 字符串了。如果没有前面声明的  object SearchResults[T] 话,在执行 as[SearchResults[Int]] 就会报错:

No Json deserializer found for type models.SearchResults[Int]. Try to implement an implicit Reads or Format for this type.

更复杂的类型

一个更为复杂的 Json 对象要能被支持的话,还须确保它自己定义的序列化和反序列化的方法:
 1case class Foo(name: String, entry: Int)
 2
 3object Foo {
 4  implicit object FopReads extends Format[Foo] {
 5    def reads(json: JsValue) = Foo(
 6      (json \ "name").as[String],
 7      (json \ "entry").as[Int])
 8    def writes(ts: Foo) = JsObject(Seq(
 9      "name" -> JsString(ts.name),
10      "entry" -> JsNumber(ts.entry)))
11  }
12}

有了这个设置后,样本类 ‘‘Foo’’ 就可以用作 ‘‘SearchResults’’ 的一部分了(Unmi: 放在中括号中,作为 SearchResults 的泛类型).
1val input = """{"page": 1,"pageSize":2, "total": 3, "elements" : [ {"name" : "foo", "entry" : 1 }, {"name" : "bar", "entry" : 2 }]}"""
2val ret = play.api.libs.json.Json.parse(input).as[SearchResults[Foo]]
永久链接 https://yanbin.blog/play2-0-tutorials-cn-json-library-with-generics/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。