ドキュメント

§JSON の基本

最新のウェブアプリケーションでは、多くの場合、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 ライブラリには、有効な JSON 型ごとに表すJsValueを拡張するケースクラスがあります。

さまざまな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(
      IndexedSeq(
        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,JsObject}

val json: JsObject = 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"
    )
  )
)

JsObjectを作成するためにJson.newBuilderを使用することもできます。

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

def asJson(active: Boolean): JsObject = {
  val builder = Json.newBuilder

  builder ++= Seq(
    "name" -> "Watership Down",
    "location" -> Json.obj(
      "lat" -> 51.235685D, "long" -> -1.309197D))

  if (active) {
    builder += "active" -> true
  }

  builder += "residents" -> Seq(
    Json.obj(
      "name" -> "Fiver",
      "age"  -> 4,
      "role" -> JsNull
    ),
    Json.obj(
      "name" -> "Bigwig",
      "age"  -> 6,
      "role" -> "Owsla"
    ))

  builder.result()
}

§Writes コンバーターの使用

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

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

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: Writes[Location] = new Writes[Location] {
  def writes(location: Location) = Json.obj(
    "lat"  -> location.lat,
    "long" -> location.long
  )
}

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

implicit val placeWrites: Writes[Place] = 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]
)(l => (l.lat, l.long))

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

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

§JsValue 構造のトラバース

JsValue構造をトラバースして、特定の値を抽出できます。構文と機能は、Scala XML処理に似ています。

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

§単純なパス \

\演算子をJsValueに適用すると、JsObjectのフィールド引数に対応するプロパティ、またはJsArrayのそのインデックスにあるアイテムが返されます。

val lat = (json \ "location" \ "lat").toOption
// returns some JsNumber(51.235685)
val bigwig = (json \ "residents" \ 1).toOption
// returns some {"name":"Bigwig","age":6,"role":"Owsla"}

\演算子はJsLookupResultを返し、これはJsDefinedまたはJsUndefinedのいずれかです。複数の\演算子をチェーンできます。中間値が見つからない場合は、結果はJsUndefinedになります。JsLookupResultgetを呼び出すと、定義されている場合は値を取得しようとしますが、定義されていない場合は例外をスローします。

オブジェクトのフィールドまたは配列のインデックスを取得するために、直接ルックアップapplyメソッド(下記)を使用することもできます。getと同様に、このメソッドは値が存在しない場合は例外をスローします。

§再帰的パス \\

\\演算子を適用すると、現在のオブジェクトとそのすべての後続要素でフィールドのルックアップが行われます。

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

§直接ルックアップ

.apply演算子を使用して、JsArrayまたはJsObjectの値を取得できます。これは、単純なパス\演算子と同一ですが、値を直接返します(JsLookupResultでラップするのではなく)、インデックスまたはキーが見つからない場合は例外をスローします。

val name = json("name")
// returns JsString("Watership Down")

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

// (json("residents")(3)
// throws an IndexOutOfBoundsException

// json("bogus")
// throws a NoSuchElementException

これは、一発限りのスクリプトやREPLなど、存在することがわかっているJSON値にアクセスする迅速で簡単なコードを作成する場合に便利です。

§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を別の型に変換する推奨される方法は、そのvalidateメソッドを使用することです(Reads型の引数を取ります)。これにより、検証と変換の両方が実行され、JsResult型が返されます。JsResultは2つのクラスによって実装されます。

検証結果を処理するために、さまざまなパターンを適用できます。

val json = { ... }

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

// Pattern matching
nameResult match {
  case JsSuccess(name, _) => println(s"Name: $name")
  case e: JsError         => println(s"Errors: ${JsError.toJson(e)}")
}

// 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(s"field: ${x._1}, errors: ${x._2}")
    }
    Option.empty[String]
  },
  valid = Some(_)
)

§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)),)

§単純なタプルの使用

単純なJSONオブジェクトは、単純なタプルとして読み書きできます。

import play.api.libs.json._

val tuple3Reads: Reads[(String, Int, Boolean)] =
  Reads.tuple3[String, Int, Boolean]("name", "age", "isStudent")

val tuple3Writes: OWrites[(String, Int, Boolean)] =
  OWrites.tuple3[String, Int, Boolean]("name", "age", "isStudent")

val tuple3ExampleJson: JsObject =
  Json.obj("name" -> "Bob", "age" -> 30, "isStudent" -> false)

val tuple3Example = Tuple3("Bob", 30, false)

tuple3Writes.writes(tuple3Example) mustEqual tuple3ExampleJson

tuple3Reads.reads(tuple3ExampleJson) mustEqual JsSuccess(tuple3Example)

次へ:HTTP と JSON


このドキュメントに誤りを見つけましたか? このページのソースコードはこちらにあります。ドキュメントガイドラインをお読みになった後、プルリクエストにご自由に貢献ください。ご質問やアドバイスがありましたら、コミュニティフォーラムでコミュニティとの会話を始めてください。