sandbox

Scala, Android, Architecture, Management, Service Design あたりを主戦場としております

Play JSON Tips

この記事は Play framework 2.x Scala Advent Calendar 2013 の19日目の記事です。

昨日登録状況を見たらまだ空いてる & 下記のツイートを見て某せらさん無双もそろそろキツそうだったので衝動的にポチりました。後悔はしていません。

そして人生において割と飲み会の優先度が高いのでこの記事も既に24時を回っております。すいません。
そんな状況でネタがないので Play JSON まわりの自分が困った小ネタの紹介です。

日付を ISO8601 形式で出力する

日付型で一般的に使われている JodaTime の DateTime を JSON 出力すると、デフォルトで Unix Timestamp なのですが、ISO8601 形式で出力したい事もあります。 Play JSON の Writes.scala に jodaDateFormat という関数が定義されており、それを使えば簡単にできます

import play.api.libs.json._
import org.joda.time._

print(Json.toJson(new DateTime(2013, 12, 19, 0, 0))) // -> 1387378800000

implicit val w = Writes.jodaDateWrites("yyyyMMdd'T'HHmmss.SSSZ")

print(Json.toJson(new DateTime(2013, 12, 19, 0, 0))) // -> "20131219T000000.000+0900"

Writes には他にも jodaLocalDateWrites なども定義されているので、他の日付系の型も同様の方法で対応できます。

値クラスをオブジェクトでなく単一の値として出力する

論理的に単一の値しか持たないものであっても、Scala では型安全性の為や他の理由から値クラスとして定義する事があります。 その場合、普通に JSON コンビネータを使うと JSObject が生成されてしまいますが、以下の様にすれば単一の JsValue として出力できます。

import play.api.libs.json._

case class UserId(id: Int) extends AnyVal
case class User(id: UserId)

implicit val userIdJsonWriter = Writes{ (userId: UserId) => JsNumber(userId.id) }
implicit val userJsonWriter = Json.writes[User]

Json.toJson(User(UserId(1))) // -> res0: play.api.libs.json.JsValue = {"id":1}}

え、何が変わってるか分からないという方向けに普通にやるとこうなるよという例が以下。

import play.api.libs.json._

case class UserId(id: Int) extends AnyVal
case class User(id: UserId)

implicit val userIdJsonWriter = Json.writes[UserId]
implicit val userJsonWriter = Json.writes[User]

Json.toJson(User(UserId(1))) // -> res0: play.api.libs.json.JsValue = {"id":{"id":1}}

尚、例では値クラスで書いていますが、シングルフィールドなのが良く値クラスであるだけであり、仕組み的には値クラスでなくても、あるクラスインスタンスを単一の JsValue として出力したい時に使えます。

いずれの例ももっとおいしい書き方があるかもしれません。
教えてください。

明日の Advent Calendar は今のところ /dev/null さんです。 (誰もいない)