概述
推荐的处理 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 注:对于以上的代码我还需要用代码来加以说明:
01020304050607080910
val
jsonString
=
""
" {"
users
":{
"
user
1
":{"
name
":"
Unmi
", "
":"
fantasia
@
sina.com
"},
"
user
2
":{"
name
":"
Unmi.cc
", "
":"
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([email protected], [email protected])对 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 对象的例子:
01
02
03
04
05
06
07
08
09
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
),
)
),
toJson(
Map(
"name"
-> toJson(
"Kiki"
),
"age"
-> toJson(
25
),
"email"
-> JsNull
)
)
)
)
)
|
上面产生的 Json 结果是:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
{
"users"
:
[
{
"name"
:
"Bob"
,
"age"
:
31.0
,
},
{
"name"
:
"Kiki"
,
"age"
:
25.0
,
"email"
:
null
}
]
}
|
序列化 Json
序列一个 JsValue
为它的 json 字符串表示格式很简单:
1
|
val
jsonString
:
String
=
Json.stringify(jsValue)
|
其他选择
虽然我们推荐用上面方案所描述的类型类(Unmi 注: typeclass 意思是?), 但是没什么能阻止用户有需求时去使用其他的 JSON 库.
例如, 这里一个小片段演示了使用基 Jerkson 库的 bundled, 反射机制,来编组原始的 scala 对象成 JSON 并进行传输:
01
02
03
04
05
06
07
08
09
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"
)
)
)
|