Documentation

You are viewing the documentation for the 2.2.0 release in the 2.2.x series of releases. The latest stable release series is 2.4.x.

§Play JSON ライブラリの基礎

§概要

JSON を使いたければ、play.api.libs.json にある JSON ライブラリを基にした Play の型クラスを使うことをおすすめします。

JSON 文字列をパースするために、Play は超高速な Java ベースの JSON ライブラリ、Jerkson を使います。

このアプローチの利点として、Scala ユーザーは Play の JSON サポートがお膳立てする特上の型安全性と関数型の側面を楽しむことができる一方、Java と Scala の両方の Play は同じ基礎となるライブラリ (Jackson) を共有することができます。

§JSON は AST (_Abstract Syntax Tree: 抽象構文木_)

例として、この JSON を見てください:

{ 
  "user": {
    "name" : "toto",
    "age" : 25,
    "email" : "[email protected]",
    "isAlive" : true,
    "friend" : {
  	  "name" : "tata",
  	  "age" : 20,
  	  "email" : "[email protected]"
    }
  } 
}

これは、以下の二つの仕組みを使った木構造として見ることができます:

正確な JSON 標準について詳しく知りたい場合は、json.org へアクセスしてください

§Json データ型

play.api.libs.json には、これらの構造を反映した 7 つの JSON データ型が含まれています。

§JsObject

標準で説明されている name/value ペアのセットです。例:

{ "name" : "toto", "age" : 45 }

§JsNull

JSON の null を表現します。

§JsBoolean

truefalse の値を取る真偽値です。

§JsNumber

JSON は short, int, long, float, double そして BigDecimal を区別しないので、BigDecimal を含む JsNumber として表現されます。Play の JSON API は、より正確な型の Scala オブジェクトに変換します。

§JsArray

配列は、あらゆる Json 値の型の列です (同じ型である必要はありません) 。例:

[ "alpha", "beta", true, 123.44, 334]

§JsString

標準的な文字列です。

§その他のデータ型

§JsUndefined

これは JSON 標準の一部ではなく、抽象構文木においてエラーとなったノードを表現するために API が内部でのみ使用します。

§JsValue

この他すべての型の親となる型です。

§JSON を使う

Play の JSON API の入り口となるのは play.api.libs.json.Json です。このクラスは次のメソッドを提供します:

§JSON 文字列のパース

どのような JSON 文字列でも JsValue として簡単にパースすることができます:

import play.api.libs.json._

val json: JsValue = Json.parse("""
{
  "user": {
    "name" : "toto",
    "age" : 25,
    "email" : "[email protected]",
    "isAlive" : true,
    "friend" : {
      "name" : "tata",
      "age" : 20,
      "email" : "[email protected]"
    }
  }
}
""")

この処理結果と対応付けられた json は、以下に続くサンプルコードで使用します。

§JSON ディレクトリの構築

§無骨な方法

上記した Json オブジェクトの例は、別の方法でも作成することができます。以下は無骨なアプローチです。

import play.api.libs.json._

JsObject(Seq(
  "users" -> JsArray(Seq(
    JsObject(Seq(
      "name" -> JsString("Bob"),
      "age" -> JsNumber(31),
      "email" -> JsString("[email protected]")
    )),
    JsObject(Seq(
      "name" -> JsString("Kiki"),
      "age" -> JsNumber(25),
      "email" -> JsNull
    ))
  ))
))

§推奨する方法

Play は JSON を作成する簡素化された文法も提供しています。上記の JsObject は以下を使って構築することができます:

import play.api.libs.json._

Json.obj(
  "users" -> Json.arr(
    Json.obj(
      "name" -> "Bob",
      "age" -> 31,
      "email" -> "[email protected]"
    ),
    Json.obj(
      "name" -> "Kiki",
      "age" -> 25,
      "email" -> JsNull
    )
  )
)

§JSON のシリアライズ

JsValue を JSON 文字列表現にシリアライズするのは簡単です:

val jsonString: String = Json.stringify(json)

§JSON ツリー内パスへのアクセス

JsValue さえ手に入れば、JSON ツリーの中を探索することができます。この API は、JsValue を探索することを除けば、Scala で NodeSeq を使って XML ドキュメントを探索するために提供されている API と似ています。

§シンプルな \ パス

\ メソッドを使ってオブジェクトのプロパティを渡り歩くことができます:

val name: JsValue = json \ "user" \ "name"

name === JsString("toto")

§再帰的な \\ パス

val emails: Seq[JsValue] = json \ "user" \\ "email"

emails === Seq(JsString("[email protected]"), JsString("[email protected]"))

§JsValue から Scala 値への変換

JSON ツリーを渡り歩いていると JsValue を見つけることができますが、JsValue から Scala の型へ変換したくなるかもしれません。
例えば、JsStringString に、または JsNumberLong に変換したくなることでしょう。

§json.as[T] による安全でない変換

JsValue をもっとも簡単に値に変換するのは、以下のようにして as[T] メソッドを使う方法です、

val name: String = (json \ "user" \ "name").as[String]

name === "toto"

しかし、このメソッドは安全ではないので、パスが見つからなかったり、変換が不可能だった場合は、エラーを含む JsResultException がスローされます。

このエラーは、期待しているであろう path.not.found を返さないことに注意してください。これは、このドキュメントの後の方で登場する JSON コンビネーターとは異なります。
これは、(json \ "user" \ "nameXXX")JsNull を返却し、暗黙の Reads[String] は検出されたエラーで説明されている通り JsString を待ち受けていることによるものです。

§Option[T] による、より安全な変換

asOpt[T] メソッドは as[T] と似ていますが、パスが見つからなかったり、変換が不可能だった場合は、例外をスローする代わりに None を返します:

val maybeName: Option[String] = (json \ "user" \ "name").asOpt[String]

maybeName === Some("toto")

§validate[T] による、もっとも安全な変換

asOpt[T] は、より安全ですが、検出されたエラー等を失ってしまいます。

validate[T] は、JsResult[T] を返すことで、JsValue をもっとも安全で堅牢な方法を提供します:

§JsResult[T] をひと言で

JsResult[T] は二つの値を持つことができます:

注意 : JsPath については後ほど説明しますが、JSON における XMLPath とまったく同じものです。
json \ "user" \ "name"
と書く場合、以下のように書くことができます :
(JsPath \ "user" \ "name")(json)
user に続けて name を検索する JsPath を作成し、これを与えられた json に適用します。

このため、成功した変換は次のように見えることでしょう:

val jsresult: JsResult[String] = (json \ "user" \ "name").validate[String]

jsresult === JsSuccess("toto")

パスが見つからない場合は、次のようになります:

val nameXXX = (json \ "user" \ "nameXXX").validate[String]

nameXXX === JsError(List((JsPath, List(ValidationError("error.expected.jsstring")))))

mapflatMap が提供されているので、内包表記を使って値を簡単に取り出すことができます:

val nameAndEmail: JsResult[(String, String)] = for {
  name <- (json \ "user" \ "name").validate[String]
  email <- (json \ "user" \ "email").validate[String]
} yield (name, email)

nameAndEmail must_== JsSuccess(("toto", "[email protected]"))

§再帰的なパス \\ の変換

\\ はサブツリーを再帰的に検索し、標準的な Scala 関数のコレクションである、発見した JsValue の Seq[JsValue] を返します。

val emails: Seq[String] = (json \ "user" \\ "email").map(_.as[String])

emails === Seq("[email protected]", "[email protected]")

§Scala 値から JsValue への変換

Scala から JSON への変換は、ちょうど T から JsValue に変換するために利用できる、暗黙的な型クラス Writes[T] に基づいた Json.toJson[T](implicit writes: Writes[T]) 関数によって行われます。

§とてもシンプルな JsValue を作る

import play.api.libs.json._

val jsonNumber = Json.toJson(4)

jsonNumber === JsNumber(4)

Play の JSON API が暗黙の Writes[Int] を提供するので、このように変換することができます

§Create a JSON array from a Seq[T]

import play.api.libs.json._

val jsonArray = Json.toJson(Seq(1, 2, 3, 4))

jsonArray === Json.arr(1, 2, 3, 4)

Play の JSON API が暗黙の Writes[Seq[Int]] を提供するので、このように変換することができます

ここで、Seq[Int] は問題なく Json 配列に変換することができました。しかし、 Seq が異質な値を含むとすると、より複雑です:

import play.api.libs.json._

val jsonArray = Json.toJson(Seq(1, "Bob", 3, 4))

これをコンパイルしようとすると、次のエラーが発生します:

No Json deserializer found for type Seq[Any]. Try to implement an implicit Writes or Format for this type.

これは 、Seq[Any] を Json に変換する手段がないためです (Any は Json でサポートされていないものまで含むことができますよね?)

シンプルな解決方法は、これを Seq[JsValue] として扱うことです:

import play.api.libs.json._

val jsonArray = Json.toJson(Seq(
  Json.toJson(1), Json.toJson("Bob"), Json.toJson(3), Json.toJson(4)
))

jsonArray === Json.arr(1, "Bob", 3, 4)

Play の JSON API が暗黙の Writes[Seq[JsValue]] を提供するので、このように変換することができます

Next: Json の読み/書き/フォーマットの結合