ScalaのJSONライブラリcirceの基本

## はじめに
Web APIをPlay Frameworkを使って開発しているが、Jsonの変換の実装がPlayのJson APIよりスマートに書けそうなcirceというライブラリの存在を知ったので、軽くメモを残したかった。

## 目的
とりあえず抑えておきたい基本的な書き方を残したいと思います。
ちょっと深ぼった内容はまた別の記事で書くつもりです。

## セットアップ
[公式のHome](https://circe.github.io/circe/)にあるQuick startを見てコピペでOK

## JSONのパース
基本は以下

- `parse`を使う
- 戻り値は`Either[ParsingFailure, Json]`
- パース可能であればRight、不可能であればLeftとなる
- `getOrElse`で失敗した場合の値を返せる

```scala
import io.circe._, io.circe.parser._

// パース可能
val rawJson = """
{
  "foo": "bar",
  "buzz": 3,
  "hoge": [1, 2, 3]
}
"""

parse(rawJson)
// => val res0: Either[io.circe.ParsingFailure,io.circe.Json] =
// Right({
//   "foo" : "bar",
//   "baz" : 123,
//   "list of stuff" : [
//     4,
//     5,
//     6
//   ]
//  })

// パース不可能
val badJson = "hoge"

parse(badJson)
// => val res1: Either[io.circe.ParsingFailure,io.circe.Json] = Left(io.circe.ParsingFailure: expected json value got 'hoge' (line 1, column 1))

// getOrElse
parse(badJson).getOrElse(Json.Null)
// => val res2: io.circe.Json = null
```

`parse`は`Either`を返すので当然match式ができる

```scala
parse(rawJson) match {
  case Left(failure) => println("Invalid JSON")
  case Right(json) => println("Yay, got some JSON")
}
// Yay, got some JSON
```

以上
実に簡単

## 値の抜き出し

- circeを使ってのJSONの値の抜き出しはcursorとか言うのを使う

まずは下準備

```scala
import cats.syntax.either._
import io.circe._, io.circe.parser._

val json: String = """
  {
    "id": "c730433b-082c-4984-9d66-855c243266f0",
    "name": "Foo",
    "counts": [1, 2, 3],
    "values": {
      "bar": true,
      "baz": 100.001,
      "qux": ["a", "b"]
    }
  }
"""

val doc: Json = parse(json).getOrElse(Json.Null)
```

ここではHcursorを使って`doc`のJSONを操作していく

```scala
val cursor: HCursor = doc.hcursor
```

どうやらこの`cursor`に対してどのキーにフォーカスをするのか指示していって、値を抜き出すっぽい
処理の順序のイメージとしてはカーソルがJSONのどこにフォーカスするかを指示し、フォーカスした場所に値があった場合は変換する感じ
最初にフォーカスされている位置はJSONツリーのルート
実際の値の抜き出しは以下で、返す型は`Decoder.Result[T]`

```scala
val baz = cursor.downField("values").downField("baz").as[Double]
// baz: Decoder.Result[Double] = Right(100.001)

// .downField("baz").as[Double]の代わりに.get[Double]("baz")で短くできる
val baz2 = cursor.downField("values").get[Double]("baz")
// baz2: Decoder.Result[Double] = Right(100.001)

// 配列の場合downArrayで最初の要素を抜き出す
val qux1 = cursor.downField("values").downField("qux").downArray.as[String]
// qux1: Decoder.Result[String] = Right("a")

// downNで指定したindexの要素を抜き出す
val qux2 = cursor.downField("values").downField("qux").downN(1).as[String]
// qux2: Decoder.Result[String] = Right("b")

// rightで次の要素を抜き出す
val qux3 = cursor.downField("values").downField("qux").downArray.right.as[String]
// qux3: Decoder.Result[String] = Right("b")

// leftで前の要素を抜き出す
val qux4 = cursor.downField("values").downField("qux").downN(1).left.as[String]
// qux4: Decoder.Result[String] = Right("a")
```

他にもどこにフォーカスするのかを指示するメソッドがあるので確認してみるといいかもしれない
ともあれ値の抜き出しについてもメソッドチェーンでできてスマート

## 値の変換
引き続き`HCursor`から操作して値を変換できるっぽい

```scala
val reversedNameCursor = cursor.downField("name").withFocus(_.mapString(_.reverse))

val reversedName: Option[Json] = reversedNameCursor.top
// reversedName: Option[Json] = Some(
//   JObject(
//     object[id -> "c730433b-082c-4984-9d66-855c243266f0",name -> "ooF",counts -> [
//   1,
//   2,
//   3
// ],values -> {
//   "bar" : true,
//   "baz" : 100.001,
//   "qux" : [
//     "a",
//     "b"
//   ]
// }]
//   )
// )
```

ちなみに`top`はフォーカスをルートへジャンプさせるやつ
変換を担った`withForcus`のシグニチャは以下

```scala
def withFocus(f: Json => Json): ACursor
```

要するにJsonを変換する処理を渡せば、そのとおりに置き換えてくれる
あまり値を変換するケースに出くわしてないので使えるか今段階思いつかないけど、簡単にできそう

## エンコーディングとデコーディング

この機能が強えからcirceを使っているまであるくらい楽
今回は自動でエンコードとデコードのやり方が定義されるのを確認していこうと思います
まずは普通にJSON化

```scala
import io.circe.syntax._

val intsJson = List(1, 2, 3).asJson
// intsJson: io.circe.Json = JArray(
//   Vector(JNumber(JsonLong(1L)), JNumber(JsonLong(2L)), JNumber(JsonLong(3L)))
// )

intsJson.as[List[Int]]
// => res0: io.circe.Decoder.Result[List[Int]] = Right(List(1, 2, 3))
```

`asJson`してエンコードし、デコードは使いたい型にただ変換すればいい
ここまでは普通だが、`asJson`はcase classにも適用可能でこれが楽

```scala
case class User(name: UseName, age: Int, address: String)
case class UseName(firstName: String, familyName: String)

val user = User(UseName("foo", "bar"), 3, "buz")
println(user)
// {
//   "name" : {
//     "firstName" : "foo",
//     "familyName" : "bar"
//   },
//   "age" : 3,
//   "address" : "buz"
// }
```

なんとネストした奴らも一気にいい感じに変換してくれる
これで大体PlayのJson.Writeをいちいち定義する手間が省けて優勝
とはいえcase classがそのままJSONにならない場合もあるが、それは個別で定義が必要、これはしょうがない
それについてはまた今度書けたら書きます

デコードについてもなかなか強い

```scala
val userJson = """
{
  "name" : {
    "firstName" : "foo",
    "familyName" : "bar"
  },
  "age" : 3,
  "address" : "buz"
}
"""

val userModel = decode[User](userJson)
// userModel: Right(User(UseName(foo,bar),3,buz))
```

やはりcase classに対してデコード可能であればいい感じに変換してくれる
もちろんimplicitはとくにこっちでは定義してない

## 終わりに
今回はここまで
次は個別にJSONに変換するための書き方を中心に書けたら書きます