Upgrade to Pro — share decks privately, control downloads, hide ads and more …

circe-codec-deck.pdf

th0rikosh1
November 10, 2018

 circe-codec-deck.pdf

th0rikosh1

November 10, 2018
Tweet

More Decks by th0rikosh1

Other Decks in Technology

Transcript

  1. 自己紹介 • 名前: 堀越 貴斗 • 会社: セプテーニ・オリジナル • 職種:

    ソフトウェアエンジニア • 関心: Scala, AWS • Twitter: @tkt_horikoshi
  2. はじめに Webアプリケーションなんかを開発していると、 例として Http Request/Response を処理するのに大抵は Json を扱いますよね。 わたしは Scala

    を触り始めてから長らく play-json と歩みを共にしてきたのですが、 最近(今更)、 circe を触ってみて大変便利でしたので実コードと解説を交えながら紹介 していこうかと思います。
  3. 準備 circe の依存関係を build.sbt に追加 val circeVersion = "0.10.1" libraryDependencies

    ++= Seq( "io.circe" %% "circe-core", "io.circe" %% "circe-generic", "io.circe" %% "circe-parser" ).map(_ % circeVersion)
  4. circe-parser に parse module が実装されている scala> val jsonString: String =

    """{ | "foo": "foo value", | "bar": { | "bar_child": "bar child value" | }, | "array":[ | { "content": 1 }, | { "content": 2 }, | { "content": 3 } | ] | }""" jsonString: String = { "foo": "foo value", "bar": { "bar child": "bar child value"
  5. Parsing Failure scala> val jsonString: String = "Invalid JSON String"

    jsonString: String = Invalid JSON String scala> val parsed = parse(jsonString) parsed: Either[io.circe.ParsingFailure,io.circe.Json] = Left(io.circe.ParsingFailure: expected json value got I (line 1
  6. Cursor という概念によって JSON を変換・抽出する scala> val jsonString = """{ |

    "id": "c730433b-082c-4984-9d66-855c243266f0", | "name": "Foo", | "values": { | "bar": true, | "baz": 100.001, | "qux": [ | "a", | "b" | ] | } | }""" scala> val doc: Json = parse(jsonString).getOrElse(Json.Null) doc: io.circe.Json = {
  7. HCursor の downField を使って抽出処理 // { // "id": "c730433b-082c-4984-9d66-855c243266f0", //

    "name": "Foo", // "values": { // "bar": true, // "baz": 100.001, // 取り出すよ!!! // "qux": [ // "a", // "b" // ] // } // } scala> val baz = cursor.downField("values").downField("baz").as[D baz: io.circe.Decoder.Result[Double] = Right(100.001)
  8. 配列の抽出 // { // "id": "c730433b-082c-4984-9d66-855c243266f0", // "name": "Foo", //

    "values": { // "bar": true, // "baz": 100.001, // "qux": [ // "a", // "b" // ] // この配列を取り出すよ!!! // } // } scala> val qux = cursor.downField("values").downField("qux").as[S qux: io.circe.Decoder.Result[Seq[String]] = Right(List(a, b))
  9. フィールドに対する変換処理をサポート (文字列を逆さに) // { // "id": "c730433b-082c-4984-9d66-855c243266f0", // "name": "Foo",

    // 文字列を逆さにするよ!!! // "values": { // "bar": true, // "baz": 100.001, // "qux": [ // "a", // "b" // ] // } // } scala> val reversedNameCursor: ACursor = | cursor.downField("name").withFocus(_.mapString(_.reverse reversedNameCursor: io circe ACursor = io circe cursor ObjectCurs
  10. Scala 標準ライブラリの大半は circe がサポート scala> import io.circe.syntax._, io.circe.parser.decode import io.circe.syntax._

    import io.circe.parser.decode scala> val json = List(1, 2, 3).asJson json: io.circe.Json = [ 1, 2, 3 ] scala> val decodedFromJson = json.as[List[Int]] decodedFromJson: io.circe.Decoder.Result[List[Int]] = Right(List( scala> val decodedFromString = decode[List[Int]](json noSpaces)
  11. import io.circe.generic.auto._ を宣言すれば case class も ※ case class のフィールドはプリミティブ型など

    circe でサポートされている必要がある scala> import io.circe.syntax._, io.circe.generic.auto._ import io.circe.syntax._ import io.circe.generic.auto._ case class Person(name: String) case class Greeting( salutation: String, person: Person, exclamationMarks: Int ) scala> val encoded = Greeting("Hey", Person("Chris"), 3).asJson encoded: io.circe.Json = { "salutation" : "Hey", "person" : {
  12. Encoder, Decoder の定義に forProductN を使うと良さそう import io.circe.{ Decoder, Encoder },

    io.circe.syntax._ case class User(id: Long, firstName: String, lastName: String) implicit val decoderUser: Decoder[User] = Decoder.forProduct3("id", "first_name", "last_name")(User) implicit val encoderUser: Encoder[User] = Encoder.forProduct3("id", "first_name", "last_name")(u => (u.id, u.firstName, u.lastName))
  13. Encode 時にスネークケースに scala> val userJson = User(123, "first name", "last

    name").asJson userJson: io.circe.Json = { "id" : 123, "first_name" : "first name", "last_name" : "last name" } scala> val decoded = userJson.as[User] decoded: io.circe.Decoder.Result[User] = Right(User(123,first nam
  14. Encoder, Decoder を独自に定義 import io.circe.{Decoder, Encoder, HCursor, Json} class Thing(val

    foo: String, val bar: Int) implicit val encoder: Encoder[Thing] = new Encoder[Thing] { final def apply(t: Thing): Json = Json.obj( ("foo", Json.fromString(s"It's a ${t.foo}")), ("bar", Json.fromInt(t.bar * 1000)) ) } implicit val decoder: Decoder[Thing] = new Decoder[Thing] { final def apply(c: HCursor): Decoder.Result[Thing] = for { foo <- c.downField("foo").as[String] bar < c downField("bar") as[Int]
  15. Codec を思いのままに scala> val thing = new Thing("test", 123) thing:

    Thing = Thing@18fdb870 scala> val encoded = thing.asJson encoded: io.circe.Json = { "foo" : "It's a test", "bar" : 123000 } scala> val decoded = encoded.as[Thing] decoded: io.circe.Decoder.Result[Thing] = Right(Thing@66b66639)
  16. まとめ Parsing Module は circe に実装済 変換・抽出には Cursor を使う Scala標準クラスの

    Codec は circe がサポート Encoder/Decoder の定義で Codec を思いのままに