0Pricing
Scala for Backend Engineering & Functional Programming · レッスン

カスタムCodec

複雑なデータ構造に対応します。

「カスタムCodec」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

カスタム Codec が必要な場合

導出で一般的なケースには対応できますが、JSON が case class と一致しない場合もあります。たとえば、レガシーなフィールド名、エンコードされた enum、タイムスタンプのような形式です。

その場合は、Decoder、Encoder、またはその両方を手作業で記述します。

カーソルから Decoder を作成する

デコーダーを構築する最も明示的な方法は、HCursor を受け取る Decoder.instance を使用することです。

downField と as でフィールドをたどり、Either を返します。

import io.circe.Decoder

case class User(name: String, age: Int)
implicit val dec: Decoder[User] = Decoder.instance { c =>
  for {
    n <- c.downField("full_name").as[String]
    a <- c.downField("years").as[Int]
  } yield User(n, a)
}

forProductN ヘルパー

単純なマッピングには、Decoder.forProduct2 のほうが簡潔です。JSON のキーを列挙し、コンストラクターを渡します。

forProduct1 から、さらに多くの引数に対応するヘルパーまで用意されています。

import io.circe.Decoder

implicit val dec: Decoder[User] =
  Decoder.forProduct2("full_name", "years")(User.apply)

手作業で Encoder を作成する

Encoder.instance を使ってデコーダーと同様に、フィールドから Json オブジェクトを構築します。

または、Encoder.forProduct2 を使えば、選択した JSON のキーへのマッピングを 1 回の呼び出しで行えます。

import io.circe.Encoder

implicit val enc: Encoder[User] =
  Encoder.forProduct2("full_name", "years")(u =>
    (u.name, u.age))

既存の Codec を変換する

ゼロから始めることはほとんどありません。map はデコーダーの出力を適用し、contramap はエンコーダーの入力を適用します。

emap は map と似ていますが失敗でき、検証結果として Either[String, A] を返します。

import io.circe.Decoder

case class Age(value: Int)
implicit val dec: Decoder[Age] =
  Decoder[Int].emap { i =>
    if (i >= 0) Right(Age(i)) else Left("negative age")
  }

Enum 用の Codec

sealed trait と case object を使って enum をモデル化できます。それぞれをタグ文字列にエンコードし、その文字列との一致によってデコードします。

emap を使うと、未知のタグを明確なデコード失敗に変換できます。

sealed trait Role
case object Admin extends Role
case object Guest extends Role

implicit val dec: Decoder[Role] = Decoder[String].emap {
  case "admin" => Right(Admin)
  case "guest" => Right(Guest)
  case other   => Left(s"unknown role: $other")
}

Enum を再びエンコードする

enum のデコーダーと、各ケースをそのタグに変換する contramap エンコーダーを組み合わせます。

これで sealed trait は、JSON 内の単純な文字列としてラウンドトリップできるようになります。

import io.circe.Encoder

implicit val enc: Encoder[Role] = Encoder[String].contramap {
  case Admin => "admin"
  case Guest => "guest"
}

日付用のカスタム Codec

Circe の core モジュールには組み込みの java.time Codec がないため、日付はカスタム Codec の典型的な対象です。

ISO 文字列形式を介してデコードとエンコードを行います。

import java.time.LocalDate
import io.circe.{Decoder, Encoder}

implicit val dec: Decoder[LocalDate] =
  Decoder[String].map(LocalDate.parse)
implicit val enc: Encoder[LocalDate] =
  Encoder[String].contramap(_.toString)

Codec にまとめる

両方向が用意できたら、Codec.from(decoder, encoder) を使って 1 つの Codec[A] にまとめます。

これにより、別々の 2 つの implicit ではなく、スコープ内の単一の implicit で済みます。

import io.circe.Codec

implicit val roleCodec: Codec[Role] =
  Codec.from(dec, enc)

ADT のディスクリミネーター

データを持つ sealed hierarchy では、circe-generic-extras を使うとラッパーオブジェクトの代わりにディスクリミネーター用フィールドを追加できます。

一度設定すれば、ADT 全体の Codec を導出できます。

import io.circe.generic.extras._

implicit val cfg: Configuration =
  Configuration.default.withDiscriminator("type")

@ConfiguredJsonCodec sealed trait Shape
@ConfiguredJsonCodec case class Circle(r: Double) extends Shape

Implicit のスコープと優先順位

手作業で記述した implicit Codec は、スコープ内にある限り導出より優先されます。常に見つかるように、コンパニオンオブジェクトに配置します。

同じ型に対して auto._ と手動のインスタンスの両方をインポートすると曖昧さが生じるため、避けてください。

理解度チェック

カスタム Codec についての理解度を確認します。

まとめ

カスタム Codec は、型と一致しない JSON を処理します。Decoder.instance、forProductN、または既存の Codec を map、contramap、emap で変換して構築します。

enum と日付は一般的な対象です。Codec.from で両方向をまとめ、ADT にはディスクリミネーターを使用します。

よくある質問

「カスタムCodec」レッスンは無料ですか?

はい。「カスタムCodec」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。

「カスタムCodec」で何を学びますか?

複雑なデータ構造に対応します。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「カスタムCodec」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?

はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. JSONを解析する
  2. ケースクラスにデコードする
  3. JSONにエンコードする
  4. カスタムCodec
← Scala for Backend Engineering & Functional Programmingに戻る