- 使用 Json
概述
推荐的处理 JSON 的方式是使用 Play 基于 JSON 库的类型类, 位置在 play.api.libs.json
.
这个库是构建于 Jerkson, 之上的,它又是基于 Java 的超快的 JSON 库 Jackson 的 Scala 封闭。
Unmi 注:在 Play 1.x 所用的 JSON 库是 Gson,而 Play 2.0 后更换成了 Jackson。还得 Play 2.0 是基于 SBT 构建的,所以 Play 2.0 的所有的 jar 都是在 $PLAY_HOME/repository/local 目录中。
这样做的好处是无论是 Java 还是 Scala 的 Play 应用依赖了相同的底层库 (Jackson), 同时 Scala 用户可以享受到 Play’s JSON 所带来的额外的类型安全性.
play.api.libs.json
包含有七种 JSON 数据类型:
JsObject
JsNull
JsUndefined
JsBoolean
JsNumber
JsArray
JsString
上面的类型都继承自通用的 JSON 值类型, JsValue
.
解析 Json 字符串
你可很轻易的解析任意的 JSON 字符串为一个 JsValue
:
1 |
val json: JsValue = Json.parse(jsonString) |
遍历 Json 树
一旦你得到了一个 JsValue
你就可以遍历这个 Json 树. 这个 API 看起来像是 Scala 使用 NodeSeq 进行遍历 XML 文档一样的
:
1 2 3 4 |
val json = Json.parse(jsonString) val maybeName = (json \ "user" \ name).asOpt[String] val emails = (json \ "user" \\ "emails").map(_.as[String]) |
注 使用 \ 来导航是不会失败的. 当用
asOpt[T]
不存在值时会返回None,所以这时候你需要自己来处理错误
. 而用as[T]
时,如果不存在值时就会报出异常.
Unmi 注:对于以上的代码我还需要用代码来加以说明:
12345678910 val jsonString = """ {"users":{"user1":{"name":"Unmi", "email":"fantasia@sina.com"},"user2":{"name":"Unmi.cc", "email":"unmi@unmi.cc"}}} """val json = Json.parse(jsonString)val maybeName = (json \ "users" \ "user1" \ "name").asOpt[String]println(maybeName)val emails = (json \ "users" \\ "email").map(_.as[String])println(emails)如果试图访问不存在的属性,如 (json \ "users" \ "user1" \ "userLaLa").asOpt[String] 时不会报错,得到的是 None 值。而换成 (json \ "users" \ "user1" \ "userLaLa").as[String] 就会有异常 [RuntimeException: String expected] 。
继续查看 println(json \ "users" \ "user1" \ "nameLaLa") 输出的是 null
上面的 Json 是 play.api.libs.json.Json,不是 play.mvc.Json, 也不是 play.libs.Json,play.libs.Json.parse() 返回的是 JsonNode,而 play.mvc.Json 没有 parse() 方法。
\ 和 \\ 是定义在 play.api.libs.json.Json 中的方法,\ 和 XPath 的 / 相似,直接的属性,\\ 和 XPath 的 // 相似,子孙属性
前面代码两个 println 语句的输出是:
Unmi
List(fantasia@sina.com, unmi@unmi.cc)
对 JSON 字符串的要求还是较严格的,没有 JavaScript 的 JSON 那么随意,属性名一定要加上双引号,单引号都不行。示例中的:
(json \ "user" \ name).asOpt[String]
name 代表的是前面定义的一个变量,如果是字面常量必须用双引号框上,否则报错:not found: value name。
转换 Scala 值为 Json
只要你有一个能够被转换为 Json 的 Scala 类型, 那就会非常容易由 Scala 值生成 Json. 例如我们来创建一个简单的 Json 对象:
1 |
val jsonNumber = Json.toJson(4) |
或者创建一个 json 数组:
1 |
val jsonArray = Json.toJson(Seq(1, 2, 3, 4)) |
这儿把 Seq[Int]
转换成 Json 数组是没有问题的. 然而,假如 Seq 包含了不同的类型值是事情就变得复杂了:
1 |
val jsonArray = Json.toJson(Seq(1, "Bob", 3, 4)) |
因为还没办法把 Seq[Any]
转换为 Json (Any
代表着任何类型,包括 Json 无法支持的类型,不是吗?)
Unmi 注: 报错为 No Json deserializer found for type Seq[Any]. Try to implement an implicit Writes or Format for this type.
一个简单的解决方案是按照 Seq[JsValue] 来处理
:
1 2 3 |
val jsonArray = Json.toJson(Seq( toJson(1), toJson("Bob"), toJson(3), toJson(4) )) |
现在我们最后来看一个创建复杂 Json 对象的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
val jsonObject = Json.toJson( Map( "users" -> Seq( toJson( Map( "name" -> toJson("Bob"), "age" -> toJson(31), "email" -> toJson("bob@gmail.com") ) ), toJson( Map( "name" -> toJson("Kiki"), "age" -> toJson(25), "email" -> JsNull ) ) ) ) ) |
上面产生的 Json 结果是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "users":[ { "name": "Bob", "age": 31.0, "email": "bob@gmail.com" }, { "name": "Kiki", "age": 25.0, "email": null } ] } |
序列化 Json
序列一个 JsValue
为它的 json 字符串表示格式很简单:
1 |
val jsonString: String = Json.stringify(jsValue) |
其他选择
虽然我们推荐用上面方案所描述的类型类(Unmi 注: typeclass 意思是?), 但是没什么能阻止用户有需求时去使用其他的 JSON 库.
例如, 这里一个小片段演示了使用基 Jerkson 库的 bundled, 反射机制,来编组原始的 scala 对象成 JSON 并进行传输:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import com.codahale.jerkson.Json._ val json = generate( Map( "url"-> "http://nytimes.com", "attributes" -> Map( "name" -> "nytimes", "country" -> "US", "id" -> 25 ), "links" -> List( "http://link1", "http://link2" ) ) ) |
本文链接 https://yanbin.blog/play2-0-tutorials-cn-play-json-library/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。