Play framework2.1でJsonを使った時のお話。
Playは2.1からJson周りがパワーアップしてまだ全容が理解できていません。
Play2.0のときはJsonはちゃんと使わなかったので、今回はちょっと調べてやってみました。
まずは下のようなモデルをJson(JsValue)に変換するとします。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// はてブのエントリーのタイトルとURL、ブックマーク数を保持 | |
case class Entry(title: String, url: String, cnt: Int) |
このインスタンスをJsValueに変換するとき、最初は以下のように書いてました。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 最初にやった書き方 | |
def toJson(entry: Entry): JsValue = { | |
Json.toJson( | |
Map( | |
"title" -> Json.toJson(entry.title), | |
"url" -> Json.toJson(entry.url), | |
"cnt" -> Json.toJson(entry.cnt) | |
) | |
) | |
} | |
// 使い方 | |
val e = Entry("hoge", "http://example.com", 30) | |
toJson(e) | |
まずMapの中の要素が各々Json.toJsonでJsValueに変換されて、その後にMapそのものもJson.toJsonでJsValueに変換してって… わけわからんわー!
Json.toJsonってのが沢山出てきてブチ切れそうになりました。
よくよく調べてみると以下のように書くといい感じっぽいです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// EntryからJsonへの変換方法の定義 | |
implicit object EntryWrites extends Writes[Entry] { | |
def writes(e: Entry): JsValue = { | |
Json.obj( | |
"title" -> e.title, | |
"url" -> e.url, | |
"cnt" -> e.cnt | |
) | |
} | |
} | |
// 使い方 | |
val e = Entry("hoge", "http://example.com", 30) | |
Json.toJson(e) | |
implicitで始まってる所がモデルをJsValueに変換する方法の定義です。toJsonとか無くて良いですね。
JsValueに変換するときにJson.toJsonを呼ぶだけです。implicitと組み合わせることで、元々あるJson.toJsonを拡張するように使用できます(既存のコードに手を加えず)。
この辺がどうなっているのか少し詳しく説明します。
まずPlay2のJson.toJsonの定義は以下のようになっています。
def toJson[T](o: T)(implicit tjs: Writes[T]): JsValue = tjs.writes(o)
Scalaを読んだことがない人には非常に分かり難いですが、これはメソッド宣言です。
defはメソッドであることを示しtoJsonがメソッド名です。[T]はジェネリクスの型パラメータです。
(o: T)は1つ目の引数で、T型のオブジェクトoを引数に取ります。
(implicit tjs: Writes[T])は2つ目の引数で、Writes[T]型のオブジェクトを引数に取ります。Writes[T]型はT型をJsValueに変換する方法が定義されてる型です。
: JsValueはこのメソッドの戻り値の型がJsValueであることを示します。
=以降にメソッドの実際の処理で、この場合はtjs.writes(o)を実行しているだけです。
長くなりましたが、このメソッドがやっていることは、T型のoをtjsの定義に基づいてJsValueに変換しているだけです。
ここで、重要なのが2番目のimplicitで始まっている引数です。
これは暗黙の引数と言われるものです。
なんと、この関数(Json.toJson)を使用するときには暗黙の引数を指定しなくても、実行出来ます!!!(なにそれコワイ&先ほどのコードにも指定されていませんでしたよね?)
実は暗黙の引数の部分を渡さないと、コンパイラがエラーを出す前にソースコード中から適しているものを探して勝手に使ってくれます。適切なものが見つからない場合はエラーになるので、安全性は高いです。
この機能により、Json.toJsonに渡す型がなんであれ、JsValueに変換可能であれば使えるようになります。
新たに関数を作るよりも、既存の関数(Json.toJson)を拡張するような感じでコーディングすることが出来るのでコード自体にも統一感が出ていいと思います。
スゴイ!!
暗黙の引数!!
0 件のコメント:
コメントを投稿