Documentation

You are viewing the documentation for the 2.6.x development release. The latest stable release series is 2.4.x.

§JSON の基本

近年の Web アプリケーションでは、JSON (JavaScript Object Notation) 形式のデータを解析・生成する必要があります。Play はこれを JSON ライブラリ でサポートしています。

JSON は軽量のデータ交換フォーマットで、次のようなものです。

{
  "name" : "Watership Down",
  "location" : {
    "lat" : 51.235685,
    "long" : -1.309197
  },
  "residents" : [ {
    "name" : "Fiver",
    "age" : 4,
    "role" : null
  }, {
    "name" : "Bigwig",
    "age" : 6,
    "role" : "Owsla"
  } ]
}

JSON の詳細については、json.org を参照してください。

§Play JSON ライブラリ

play.api.libs.json パッケージには、JSON データを表現するためのデータ構造と、これらのデータ構造と他のデータ表現との間で変換を行うためのユーティリティが含まれています。重要な型は次のとおりです。

§JsValue

これは JSON の値を表すトレイトです。JSON ライブラリには以下のような、JsValue を拡張した有効な JSON 型を表すケースクラスがあります。

さまざまな JsValue 型を使用して、任意の JSON 構造の表現を構築することができます。

§Json

Json オブジェクトは、主に JsValue 構造との変換に用いるユーティリティを提供します。

§JsPath

XML に対する XPath のような、JsValue 構造へのパスを表します。これは、JsValue 構造を走査するために使うものであり、暗黙のコンバータパターン内にて使われています。

§JsValue への変換

§文字列解析の使用

import play.api.libs.json._

val json: JsValue = Json.parse("""
{
  "name" : "Watership Down",
  "location" : {
    "lat" : 51.235685,
    "long" : -1.309197
  },
  "residents" : [ {
    "name" : "Fiver",
    "age" : 4,
    "role" : null
  }, {
    "name" : "Bigwig",
    "age" : 6,
    "role" : "Owsla"
  } ]
}
""")

§クラス構築の使用

import play.api.libs.json._

val json: JsValue = JsObject(Seq(
  "name" -> JsString("Watership Down"),
  "location" -> JsObject(Seq("lat" -> JsNumber(51.235685), "long" -> JsNumber(-1.309197))),
  "residents" -> JsArray(Seq(
    JsObject(Seq(
      "name" -> JsString("Fiver"),
      "age" -> JsNumber(4),
      "role" -> JsNull
    )),
    JsObject(Seq(
      "name" -> JsString("Bigwig"),
      "age" -> JsNumber(6),
      "role" -> JsString("Owsla")
    ))
  ))
))

Json.objJson.arr は構築を少しだけ簡単にできます。ほとんどの値は JsValue クラスで明示的にラップする必要はないことに注意してください。ファクトリメソッドは暗黙の変換を使用します (詳細は後述) 。

import play.api.libs.json.{JsNull,Json,JsString,JsValue}

val json: JsValue = Json.obj(
  "name" -> "Watership Down",
  "location" -> Json.obj("lat" -> 51.235685, "long" -> -1.309197),
  "residents" -> Json.arr(
    Json.obj(
      "name" -> "Fiver",
      "age" -> 4,
      "role" -> JsNull
    ),
    Json.obj(
      "name" -> "Bigwig",
      "age" -> 6,
      "role" -> "Owsla"
    )
  )
)

§書き込みコンバータの使用

Scala から JsValue への変換は、ユーティリティメソッド Json.toJson[T](T)(implicit writes: Writes[T]) によって実行されます。この機能は TJsValue に変換する Writes[T] 型のコンバータに依存します。

Play JSON API は、IntDoubleStringBoolean などのほとんどの基本型に対して暗黙の Writes を提供します。Writes[T] が存在するあらゆる T 型について、これらのコレクションの Writes もサポートしています。

import play.api.libs.json._

// basic types
val jsonString = Json.toJson("Fiver")
val jsonNumber = Json.toJson(4)
val jsonBoolean = Json.toJson(false)

// collections of basic types
val jsonArrayOfInts = Json.toJson(Seq(1, 2, 3, 4))
val jsonArrayOfStrings = Json.toJson(List("Fiver", "Bigwig"))

独自のモデルを JsValue に変換するには、暗黙の Writes コンバータを定義し、それらをスコープ内に提供する必要があります。

case class Location(lat: Double, long: Double)
case class Resident(name: String, age: Int, role: Option[String])
case class Place(name: String, location: Location, residents: Seq[Resident])
import play.api.libs.json._

implicit val locationWrites = new Writes[Location] {
  def writes(location: Location) = Json.obj(
    "lat" -> location.lat,
    "long" -> location.long
  )
}

implicit val residentWrites = new Writes[Resident] {
  def writes(resident: Resident) = Json.obj(
    "name" -> resident.name,
    "age" -> resident.age,
    "role" -> resident.role
  )
}

implicit val placeWrites = new Writes[Place] {
  def writes(place: Place) = Json.obj(
    "name" -> place.name,
    "location" -> place.location,
    "residents" -> place.residents)
}

val place = Place(
  "Watership Down",
  Location(51.235685, -1.309197),
  Seq(
    Resident("Fiver", 4, None),
    Resident("Bigwig", 6, Some("Owsla"))
  )
)

val json = Json.toJson(place)

代わりに、コンビネータパターンを使って Writes を定義することもできます。

メモ: コンビネータパターンについては、JSON Reads/Writes/Formats コンビネータ で詳しく説明しています。

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val locationWrites: Writes[Location] = (
  (JsPath \ "lat").write[Double] and
  (JsPath \ "long").write[Double]
)(unlift(Location.unapply))

implicit val residentWrites: Writes[Resident] = (
  (JsPath \ "name").write[String] and
  (JsPath \ "age").write[Int] and
  (JsPath \ "role").writeNullable[String]
)(unlift(Resident.unapply))

implicit val placeWrites: Writes[Place] = (
  (JsPath \ "name").write[String] and
  (JsPath \ "location").write[Location] and
  (JsPath \ "residents").write[Seq[Resident]]
)(unlift(Place.unapply))

§JsValue 構造の走査

JsValue 構造を走査して特定の値を抽出できます。構文と機能は Scala の XML 処理と似ています。

メモ: 次の例は、前の例で作成された JsValue 構造に適用されます。

§基本的なパス \

\ 演算子を JsValue に適用すると、これが JsObject であると仮定して、フィールド引数に対応するプロパティを返します。

val lat = (json \ "location" \ "lat").get
// returns JsNumber(51.235685)

§再帰的パス \\

\\ 演算子を適用すると、現在のオブジェクトとすべての子孫のフィールドを検索します。

val names = json \\ "name"
// returns Seq(JsString("Watership Down"), JsString("Fiver"), JsString("Bigwig"))

§インデックス検索 (JsArrays の場合)

インデックス番号付きの apply 演算子を使用して、JsArray の中の値を検索できます。

val bigwig = (json \ "residents")(1)
// returns {"name":"Bigwig","age":6,"role":"Owsla"}

§JsValue からの変換

§文字列ユーティリティの使用

縮小版:

val minifiedString: String = Json.stringify(json)
{"name":"Watership Down","location":{"lat":51.235685,"long":-1.309197},"residents":[{"name":"Fiver","age":4,"role":null},{"name":"Bigwig","age":6,"role":"Owsla"}]}

可読版:

val readableString: String = Json.prettyPrint(json)
{
  "name" : "Watership Down",
  "location" : {
    "lat" : 51.235685,
    "long" : -1.309197
  },
  "residents" : [ {
    "name" : "Fiver",
    "age" : 4,
    "role" : null
  }, {
    "name" : "Bigwig",
    "age" : 6,
    "role" : "Owsla"
  } ]
}

§JsValue.as/asOpt の使用

JsValue を別の型に変換する最も簡単な方法は、JsValue.as[T](implicit fjs: Reads[T]): T を使用することです。これには、JsValueT に変換する型 Reads[T] (Writes[T] の逆) の暗黙のコンバータが必要です。Writes と同様に、JSON API は基本的な型の Reads を提供します。

val name = (json \ "name").as[String]
// "Watership Down"

val names = (json \\ "name").map(_.as[String])
// Seq("Watership Down", "Fiver", "Bigwig")

パスが見つからないか、変換が不可能な場合、as メソッドは JsResultException をスローします。より安全なメソッドは、JsValue.asOpt[T](implicit fjs: Reads[T]): Option[T] です。

val nameOption = (json \ "name").asOpt[String]
// Some("Watership Down")

val bogusOption = (json \ "bogus").asOpt[String]
// None

asOpt メソッドはより安全ですが、エラー情報は失われます。

§バリデーションの使用

JsValue から別の型に変換するための好ましい方法は、(Reads 型の引数を取る) validate メソッドを使うことです。これはバリデーションと変換の両方を実行し、JsResult の型を返します。JsResult は2つのクラスによって実装されます。

バリデーション結果を処理するためのさまざまなパターンを適用できます。

val json = { ... }

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

// Pattern matching
nameResult match {
  case s: JsSuccess[String] => println("Name: " + s.get)
  case e: JsError => println("Errors: " + JsError.toFlatJson(e).toString())
}

// Fallback value
val nameOrFallback = nameResult.getOrElse("Undefined")

// map
val nameUpperResult: JsResult[String] = nameResult.map(_.toUpperCase())

// fold
val nameOption: Option[String] = nameResult.fold(
  invalid = {
    fieldErrors => fieldErrors.foreach(x => {
      println("field: " + x._1 + ", errors: " + x._2)
    })
    None
  },
  valid = {
    name => Some(name)
  }
)

§JsValue からモデルへ

JsValue からモデルに変換するには、T がモデルの型である暗黙の Reads[T] を定義しなければなりません。

メモ: Reads と独自のバリデーションの実装に使われるパターンは、JSON Reads/Writes/Formats コンビネータ で詳しく説明しています。

case class Location(lat: Double, long: Double)
case class Resident(name: String, age: Int, role: Option[String])
case class Place(name: String, location: Location, residents: Seq[Resident])
import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val locationReads: Reads[Location] = (
  (JsPath \ "lat").read[Double] and
  (JsPath \ "long").read[Double]
)(Location.apply _)

implicit val residentReads: Reads[Resident] = (
  (JsPath \ "name").read[String] and
  (JsPath \ "age").read[Int] and
  (JsPath \ "role").readNullable[String]
)(Resident.apply _)

implicit val placeReads: Reads[Place] = (
  (JsPath \ "name").read[String] and
  (JsPath \ "location").read[Location] and
  (JsPath \ "residents").read[Seq[Resident]]
)(Place.apply _)


val json = { ... }

val placeResult: JsResult[Place] = json.validate[Place]
// JsSuccess(Place(...),)

val residentResult: JsResult[Resident] = (json \ "residents")(1).validate[Resident]
// JsSuccess(Resident(Bigwig,6,Some(Owsla)),)

Next: JSON と HTTP


このドキュメントの翻訳は Play チームによってメンテナンスされているものではありません。 間違いを見つけた場合、このページのソースコードを ここ で確認することができます。 ドキュメントガイドライン を読んで、お気軽にプルリクエストを送ってください。