次に、play.api.libs.json.Jsonオブジェクトが持っている関数を見てみましょう。このオブジェクトが持つ関数を使うと、JsValueとString、JsValueとScalaオブジェクトの相互変換ができます。
受け取ったString型文字列をJsValueにパースできます。
scala> import play.api.libs.json.Json scala> val jsVal = Json.parse("""{"name":"taro"}""")
parse関数とは逆に、引数として受け取ったJsValueを文字列にできます。なお、この関数で作成した文字列は改行とインデントがありません。整形された文字列が必要な場合、prettyPrint関数を使用してください。
scala> import play.api.libs.json.Json scala> val jsStr = Json.stringify(JsObject("name" -> JsString("taro") :: Nil))
暗黙の引数である、Writes[T]を用いてScalaオブジェクトからJsValueへ変換を行います。
scala> import play.api.libs.json.Json scala> val jsValStr = Json.toJson("taro") //JsValue = "taro" scala> val jsValInt = Json.toJson(1) //JsValue = 1 scala> val m = Map("name"-> "taro","email" -> "taro@taro.com") scala> val jsValMap = Json.toJson(m) //JsValue = {"name":"taro","email":"taro@taro.com"}
上記サンプルでは、IntやString、Mapに対してtoJson関数を実行し、ScalaのオブジェクトからJsValueオブジェクトへ変換しています。IntやString、Mapオブジェクトに関しては、暗黙のWritesオブジェクトを提供してくれるので、そのまま変換が可能です。
ただし、Mapに関しては「Writes[Map[String,T]]」型であれば変換は可能ですが、「Map("name" -> "taro","age" -> 30)」など、異なる型の値が混合したMapの場合には適用できないので注意してください。
暗黙引数である、「Reads[T]」を用いてJsValueからScalaオブジェクトへの変換を行います。
scala> import play.api.libs.json.Json scala> val jStr = JsString("taro") scala> val res = Json.fromJson[String](jStr) //play.api.libs.json.JsResult[String] = JsSuccess(taro,) //Json.fromJson[Int](jStr)とした場合、JsErrorが返る
上記サンプルでは、fromJson関数を使用してString型に変換しています。なお、変換したい型に対して、Readsトレイトをミックスインしたオブジェクトを用意することにより、任意の変換が可能になります。
XMLにおいて、XMLオブジェクトに対して要素や属性の位置を指定するために用いる言語、「XMLPath」がありますが、JSONは抽象構文木なので、XMLに対する構文と似た言語を適用できます。これは「JsPath」と呼ばれ、JSONツリーを解析できます。
では、次のようなJsValueオブジェクトを用意し、JsPathを用いてアクセスしてみましょう。
scala> import play.api.libs.json._ scala> val json = Json.parse(""" { "name":"taro", "age" :30, "address": { "country":{ "name":"japan", "language":"japanese" }, "city":"tokyo" }, "language" : ["Scala","Java"] } """)
JsPathでは、「\」か「\\」を用いてJSONにアクセスします。まずはシンプルにnameプロパティ、address内のcountryプロパティにアクセスしてみましょう。
scala> json \ "name" //結果:"taro" scala> json \ "address" \ "country" //結果:{"name":"japan","language":"japanese"}
配列にアクセスする場合、インデックス指定が可能です。
scala> (json \ "language")(1) //結果:Java \\を使用すると、再帰的にそのプロパティを探索して結果を Seq[JsValue]に格納して返します。 // 複数の/再帰的なパス scala> json \\ "language" //結果:List("japanese", ["Scala","Java"])
JsPathを使ってJsValueを探索した結果、それをScalaオブジェクトに変換することもあるでしょう。変換方法はいくつかあるので、それらの方法を紹介します。
as関数を使用すれば、簡単に変換を行うことが可能です。型パラメータで変換したい型を指定します。
//パス探索と変換に成功するケース scala> val name = (json \ "name").as[String]
上記例ではnameの値がStringに変換されますが、次のようにパスが見つからなかった場合や変換できなかった場合、JsResultException例外が発生します。
//パスが存在しないケース scala> val name = (json \ "notfoundName").as[String] play.api.libs.json.JsResultException: : : //型変換に失敗するケース scala> val name = (json \ "name").as[Int] play.api.libs.json.JsResultException: : :
asによる変換は、場合によっては例外が発生していました。Playには、変換時に型のエラーが発生した場合、Noneを返してくれるasOpt[T]が用意されています。
//パス探索と変換に成功するケース scala> val name = (json \ "name").asOpt[String] name: Option[String] = Some(taro) //パスが存在しないケース scala> val name = (json \ "notfoundName").asOpt[String] name: Option[String] = None //型変換に失敗するケース scala> val name = (json \ "name").asOpt[Int] name: Option[Int] = None
as[T]よりasOpt[T]の方が安全に使用できますね。
asOpt[T]はasよりも安全ですが、検出されたエラー情報を失ってしまいます。validate[T]関数を使用すれば、エラー発生時にその情報を持ったまま結果を返せます。
変換に成功した場合は「JsSuccess」、失敗した場合は「JsError」を返すため、それぞれに応じた処理を記述できます。
//パス探索と変換に成功するケース scala> (json \ "name").validate[String].fold( valid = ( s => println("success!!:" + s)), invalid = ( e => println("error!!:" + e))) success!!:taro
//型変換に失敗するケース scala> (json \ "name").validate[Int].fold( valid = ( s => println("success!!:" + s) ), invalid = ( e => println("error!!:" + e) )) error!!:List((,List(ValidationError(validate.error.expected.jsnumber,WrappedArray()))))
ここまでは、JSON形式のデータ型、JsPathによるアクセス方法や変換方法を紹介してきました。最近のアプリではJSON形式でデータを送受信することが多くなってきているので、使う機会も多そうですね。
なお、本記事では独自で定義したScalaオブジェクトとJSONデータのマッピングについては解説していません。その機能を実現したい場合、ドキュメント「ScalaJsonCombinators」にあるように、Reads[T]/Writes[T]/Format[T]コンビネータを使用するので、必要があれば確認してみてください。
Copyright © ITmedia, Inc. All Rights Reserved.