Play 2.0 中文資料 - Play JSON 庫
2013-01-13 — Yanbin概述
推薦的處理 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" ) ) )
|