circeはJsonライブラリ。Argonautをforkしたもので、CatsやShapelessを使っている。Jawnという速いJsonパーサを使っているとのこと。
Argonautは触ったことないので比較してない。(・ω・`)
Encode
import io.circe._
import io.circe.syntax._
import io.circe.generic.auto._
scala> case class User(id: Int, name: String)
//defined class User
scala> User(1, "taro").asJson
//res79: io.circe.Json =
//{
// "id" : 1,
// "name" : "taro"
//}
なうい(・ω・`)
もう少し複雑に。
scala> case class Group(users: Seq[User], name: Option[String])
//defined class Group
scala> Group(Seq(User(1, "taro"), User(2, "sato")), Some("fuga")).asJson
//res84: io.circe.Json =
//{
// "users" : [
// {
// "id" : 1,
// "name" : "taro"
// },
// {
// "id" : 2,
// "name" : "sato"
// }
// ],
// "name" : "fuga"
//}
Encoderの型クラスのインスタンスを定義すれば自動導出時に使ってくれる。DateTime系のインスタンスとか必要になりそう。
Decode
パースしてUserに詰める。
scala> parser.decode[User]("""{"id":1, "name":"taro"}""")
//res95: cats.data.Xor[io.circe.Error,User] = Right(User(1,taro))
失敗時。
scala> parser.decode[User]("""{"id":1, "name_fuga":"taro"}""")
//res96: cats.data.Xor[io.circe.Error,User] = Left(io.circe.DecodingFailure: Attempt to decode value on failed cursor: El(DownField(name),false))
XorはEitherみたいなもの。
Cursor
jsonを走査したりset/deleteしたりできる。
移動してidの値を取ってみる。
scala> val userJson: Json = User(1, "taro").asJson
scala> userJson.hcursor.get[Int]("id")
//res21: io.circe.Decoder.Result[Int] = Right(1)
配列から最初のオブジェクト取得。
scala> val groupJson = Group(Seq(User(1, "taro"), User(2, "sato")), Some("fuga")).asJson
//groupJson: io.circe.Json =
//{
// "users" : [
// {
// "id" : 1,
// "name" : "taro"
// },
// {
// "id" : 2,
// "name" : "sato"
// }
// ],
// "name" : "fuga"
//}
scala> .hcursor.downField("users").downArray.first.as[User]
//res23: io.circe.Decoder.Result[User] = Right(User(1,taro))
hcursorのhはhistoryで操作履歴を持っているようだ。
履歴から再現したりできるもよう。
scala> val histories = groupJson.hcursor.downField("users").downArray.first.any.history
//histories: List[io.circe.HistoryOp] = List(El(MoveFirst,true), El(DownArray,true), El(DownField(users),true))
scala> groupJson.cursor.replay(histories).map(_.as[User])
//res26: Option[io.circe.Decoder.Result[User]] = Some(Right(User(1,taro)))
使いどころ難しそう(・ω・`)
Incomplete
decodeするとき、値が全部なくても後で渡すことができる。
普通にdecodeするとidがないのでエラー。
scala> parser.decode[User]("""{"name":"taro"}""")
//res98: cats.data.Xor[io.circe.Error,User] = Left(io.circe.DecodingFailure: Attempt to decode value on failed cursor: El(DownField(id),false))
idは後から渡すことに。
scala> parser.decode[Int => User]("""{"name":"taro"}""")
//res99: cats.data.Xor[io.circe.Error,Int => User] = Right(<function1>)
scala> .map(_(1))
//res100: cats.data.Xor[io.circe.Error,User] = Right(User(1,taro))
idという名前でちゃんと書きたいときは、
scala> import shapeless.Witness
//import shapeless.Witness
scala> import shapeless.labelled.{FieldType, field}
//import shapeless.labelled.{FieldType, field}
scala> parser.decode[FieldType[Witness.`'id`.T, Int] => User]("""{"name":"taro"}""")
//res105: cats.data.Xor[io.circe.Error,shapeless.labelled.FieldType[Symbol with shapeless.tag.Tagged[String("id")],Int] => User] = Right(<function1>)
scala> .map(_(field(1)))
//res106: cats.data.Xor[io.circe.Error,User] = Right(User(1,taro))
よさげ(・ω・`)