はじめに
みなさんScalaでJSON扱うとき何を使っていますか。ぼくは最近までjson4sを使っていたのですが、コア部分でアクロバティックなことをした結果としてメンテが難しい状況になっていると聞いて、割と近しい代替手段を探していましたが、Circeが良さそうなので新プロジェクトはこちらに移行してます。
Circeの特徴は
- case classの自動フォーマット(case class用のフォーマッタが不要
- Scala2.12以降でないとマトモに動かない
- cats依存
どんな感じよ
とりあえず公式サイトからのコピペ。まずbuild.sbtに以下の依存を追加。
val circeVersion = "0.8.0"
libraryDependencies ++= Seq(
"io.circe" %% "circe-core",
"io.circe" %% "circe-generic",
"io.circe" %% "circe-parser"
).map(_ % circeVersion)
基本的な使い方
import io.circe._
import io.circe.generic.auto._
import io.circe.parser._
import io.circe.syntax._
sealed trait Foo
// defined trait Foo
case class Bar(xs: List[String]) extends Foo
// defined class Bar
case class Qux(i: Int, d: Option[Double]) extends Foo
// defined class Qux
val foo: Foo = Qux(13, Some(14.0))
// foo: Foo = Qux(13,Some(14.0))
foo.asJson.noSpaces
// res0: String = {"Qux":{"i":13,"d":14.0}}
decode[Foo](foo.asJson.spaces4)
// res1: Either[io.circe.Error,Foo] = Right(Qux(13,Some(14.0)))
発展的に自前でScala的enum型のEncoderを書いてみようとしましたが、もっと詳しい記事があったのでそっちを参照。
Playframeworkとの連携
play-circeでいいんじゃないかな。
controllerはこんな感じに
package controllers
import javax.inject.{Inject, Singleton}
import io.circe.generic.auto._
import io.circe.syntax._
@Singleton
class LogController @Inject()(val cc: ControllerComponents) extends AbstractController(cc) with Circe {
def show(id: Long) = Action {
Log.findById(id).fold(NotFound(s"Not found id=${id}")) { log => Ok(log.asJson) }
}
def create() = Action(circe.tolerantJson[CreateLog]) { req =>
if(Log.create(req.body) > 0) Success else SQLError
}
}
OkにcirceのJSON型を突っ込めば返り値として使えるし、tolerantJsonとかで型を指定してやればPOSTメソッドでJSONを受け取るようにできます。play-json4sより新しいだけあってシンプルかなと。
最後に
Circe割と気に入ったので布教したくて色々書こうとしたけどコピペとリンクだけになってしまいましたという酷い話です。本当にすみません。みんなCirceで快適なScalaライフを!