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、id
はOption型
となる。
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 => {} )
入れ子の解釈構文さえ解ればそこまで難しくないと思います。