PlayFrameworkではplay-jsonライブラリを使うことでJsonを簡単にパースすることができますが、今回nullの含まれるListをパースする時に少しはまったのでメモとして残しておきます。
play-jsonに関しては既に色々な方が説明記事を挙げてくださっています。
http://qiita.com/daneko0123/items/1f19f83b943cfd3b8829
http://dev.classmethod.jp/server-side/scala/play-json-5-frequent-patterns/
環境
- Play2.5.1
概要
今回、以下のようなnullが含まれるリストをSeq[Option[String]]の型にパースしようとしました。
{
"id":"aaa",
"age":18,
"params":[
"test1",
null,
"test2"
]
}
そこでパース用のクラスとして以下を用意したのですが、コンパイル時にエラーが出てしまいました。
case class Sample(id:String, age:Int, params:Seq[Option[String]])
object Sample extends ((String,Int,Seq[Option[String]]) => Sample) {
implicit val SampleRead: Reads[Sample] = (
(JsPath \ "id").read[String] and
(JsPath \ "age").read[Int] and
(JsPath \ "params").read[Seq[Option[String]]] | Reads.pure(Seq.empty[Option[String]])
)(Sample)
}
No Json deserializer found for type Seq[Option[String]]. Try to implement an implicit Reads or Format for this type.
[error] (JsPath \ "params").read[Seq[Option[String]]] | Reads.pure(Seq.empty[Option[String]])
解決策
自分も完全に理解できているわけではないのですが、read[A]には暗黙的にReads[A]が渡されており、今回read[Seq[Option[String]]]に対応するReads[Seq[Option[String]]]の実装が無かったためエラーが出ていたようです。
というわけで、以下のように実装を追加しました。
case class Sample(id:String, age:Int, params:Seq[Option[String]])
object Sample extends ((String,Int,Seq[Option[String]]) => Sample) {
implicit val SeqOpStringRead = new Reads[Seq[Option[String]]] {
override def reads(json: JsValue): JsResult[Seq[Option[String]]] = {
json match {
case JsArray(seq) => JsSuccess(seq.map(jsvalue => jsvalue.asOpt[String]))
case _ => JsError("Invalid array")
}
}
}
implicit val SampleRead: Reads[Sample] = (
(JsPath \ "id").read[String] and
(JsPath \ "age").read[Int] and
(JsPath \ "params").read[Seq[Option[String]]] | Reads.pure(Seq.empty[Option[String]])
)(Sample)
}
これでコンパイルは通るようになり、以下のようにパースできました。
Sample(aaa,18,ListBuffer(Some(test1), None, Some(test2)))
以上です。Scala+Playはまだあまり詳しくないので、間違っている所やもっと良い方法などあれば教えていただけると助かります!