カスタム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 ShapeImplicit のスコープと優先順位
手作業で記述した 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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- JSONを解析する
- ケースクラスにデコードする
- JSONにエンコードする
- カスタムCodec