Playframework 複雑なJsonデータをオブジェクトにmappingまたはForm bind、validateする

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

スポンサードリンク

WebAPIとして動作しているPlayframeworkのJsonデータのパースについて。

Jsonデータは、

{
  "shopName":"Let it Ride",
  "position":{
    "lat":35.66530148846749,
    "lng":139.41634567694945
  },
  "testArray":[1,2,3,4]
}

のように入れ子、または配列のデータを含むものをパースする。


case classの定義

上記のJsonをcalse classにmappingする。入れ子になっているpositionはcase classを入れ子にしてある。またJsonに存在しないkey、idOption型となる。

case classの定義

case class Position( lat:BigDecimal, lon:BigDecimal) //lngとlonと名称が違うプロパティにmapします。
case class Shop (
                 id:Option[Long],
                 name:String = "",
                 position: Position,
                 vs: List[Int]
                )


オブジェクトにmappingする


mappingの定義

mappingはReadsの実装で定義する。Shopオブジェクトの初期化にJsonを解釈したデータをセットする。

implicit val shopFormat = new Reads[Shop]{
  def reads(json: JsValue): JsResult[Shop] = JsSuccess( Shop(
    id = (json \ "id").asOpt[Long],
    name = (json \ "shopName").as[String],
    position = Position(
      (json \ "position" \ "lat").as[BigDecimal],
      (json \ "position" \ "lng").as[BigDecimal]
    ),
    vs =  (json \ "testArray").as[List[Int]]
  ))
}

"position": {"lat":..., "lng":...}のような入れ子構造の解釈は(json \ "position" \ "lat")と記述して解釈できる。

配列は(json \ "testArray").as[List[Int]]として解釈できる。尚、IDEにNo implicit found for parameter jfs Reads[List[Int]]と表示されるが問題なく実行できた。


parseの実行

JsValueに対して、validate & 対象のclassの型パラメータを指定すればよい。

Json.parse( jsonString ).validate[Shop].get


Formオブジェクトを通してbindする


mappingの定義

Formオブジェクトを通してbindすれば、HttpRequestと同様にバリデートしながらオブジェクトにmappingできる。

以下のように入れ子のmappingを定義することで、Json構造を解釈できる。

val positionMapping = mapping(
  "lat"  -> bigDecimal,
  "lng" -> bigDecimal
)(Position.apply)(Position.unapply)

val shopForm = Form{
  mapping(
    "id" -> optional(longNumber),
    "shopName" -> nonEmptyText,
    "position" -> positionMapping,
    "testArray" -> list(number)
  )(Shop.apply)(Shop.unapply)
}


Jsonデータのバリデートと結果の確認

bindメソッドはJsValueをオーバーロードしているので、バリデートは通常のリクエスト同様に記述できる。

shopForm.bind( Json.parse( jsonString ) ).fold(
  hasErrors => {},
  success => {}
)

入れ子の解釈構文さえ解ればそこまで難しくないと思います。