連載
» 2013年09月25日 18時00分 公開

Play2におけるJSONおよびCoffeeScriptの使い方Scala+Play 2.0でWebアプリ開発入門(9)(2/3 ページ)

[中村修太,クラスメソッド]

Jsonオブジェクトの関数を使用する

 次に、play.api.libs.json.Jsonオブジェクトが持っている関数を見てみましょう。このオブジェクトが持つ関数を使うと、JsValueとString、JsValueとScalaオブジェクトの相互変換ができます。

Json.parse

 受け取ったString型文字列をJsValueにパースできます。

scala> import play.api.libs.json.Json
scala> val jsVal = Json.parse("""{"name":"taro"}""")

Json.stringify

 parse関数とは逆に、引数として受け取ったJsValueを文字列にできます。なお、この関数で作成した文字列は改行とインデントがありません。整形された文字列が必要な場合、prettyPrint関数を使用してください。

scala> import play.api.libs.json.Json
scala> val jsStr = Json.stringify(JsObject("name" -> JsString("taro") :: Nil))

Json.toJson[T](t: T)(implicit writes: Writes[T])

 暗黙の引数である、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の場合には適用できないので注意してください。

Json.fromJson[T](json: JsValue)(implicit reads: Reads[T]) :

 暗黙引数である、「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トレイトをミックスインしたオブジェクトを用意することにより、任意の変換が可能になります。

「JsPath」を用いたツリー内へのアクセス

 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の探索結果をScalaオブジェクトに変換する

 JsPathを使ってJsValueを探索した結果、それをScalaオブジェクトに変換することもあるでしょう。変換方法はいくつかあるので、それらの方法を紹介します。

as[T]関数による変換

 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: 
:
:

asOpt[T]による変換

 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]の方が安全に使用できますね。

validate[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()))))

独自で定義したScalaオブジェクトとJSONデータのマッピング

 ここまでは、JSON形式のデータ型、JsPathによるアクセス方法や変換方法を紹介してきました。最近のアプリではJSON形式でデータを送受信することが多くなってきているので、使う機会も多そうですね。

 なお、本記事では独自で定義したScalaオブジェクトとJSONデータのマッピングについては解説していません。その機能を実現したい場合、ドキュメント「ScalaJsonCombinators」にあるように、Reads[T]/Writes[T]/Format[T]コンビネータを使用するので、必要があれば確認してみてください。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。