circe触ってみた

[circe](https://github.com/travisbrown/circe)はJsonライブラリ。[Argonaut](https://github.com/argonaut-io/argonaut)をforkしたもので、CatsやShapelessを使っている。[Jawn](https://github.com/non/jawn)という速いJsonパーサを使っているとのこと。

Argonautは触ったことないので比較してない。(･ω･`)


## Encode

```scala
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
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系のインスタンス](https://gist.github.com/travisbrown/afbc0821691602c7824e)とか必要になりそう。

## Decode

パースしてUserに詰める。

```scala
scala> parser.decode[User]("""{"id":1, "name":"taro"}""")
//res95: cats.data.Xor[io.circe.Error,User] = Right(User(1,taro))
```

失敗時。

```scala
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
scala> val userJson: Json = User(1, "taro").asJson

scala> userJson.hcursor.get[Int]("id")
//res21: io.circe.Decoder.Result[Int] = Right(1)
```

配列から最初のオブジェクト取得。

```scala
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
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
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
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
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))
```

よさげ(･ω･`)