0Pricing
Scala for Backend Engineering & Functional Programming · 课时

自定义编解码器

处理复杂的数据形状。

自定义编解码器 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

何时需要自定义编解码器

派生可以覆盖常见情况,但有时 JSON 的结构并不反映您的样例类,例如字段名称来自旧系统、枚举经过编码,或者使用时间戳等格式。

这时,您需要手动编写 Decoder、Encoder,或同时编写两者。

从游标创建解码器

构建解码器最明确的方式是使用 Decoder.instance 接收一个 HCursor。

您可以使用 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.instance 与解码器对应起来,根据您的字段构建一个 Json 对象。

或者使用 Encoder.forProduct2,通过一次调用将字段映射回指定的 JSON 键。

import io.circe.Encoder

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

转换现有编解码器

您很少需要从零开始。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")
  }

枚举的编解码器

带有样例对象的密封特征可以用来表示枚举。将每个枚举值编码为标签字符串,然后通过匹配该字符串进行解码。

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")
}

将枚举重新编码

将枚举解码器与使用 contramap 创建的编码器配对,让编码器将每个枚举值转换为相应标签。

现在,这个密封特征在 JSON 中可以作为普通字符串完成往返转换。

import io.circe.Encoder

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

日期的自定义编解码器

Circe 核心模块没有内置的 java.time 编解码器,因此日期是使用自定义编解码器的典型场景。

通过 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) 将它们组合为一个 Codec[A]。

这样只需在作用域中保留一个隐式值,而不是两个彼此独立的隐式值。

import io.circe.Codec

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

ADT 的判别字段

对于包含数据的密封层次结构,circe-generic-extras 会添加判别字段,而不是使用包装对象。

只需配置一次,即可为整个 ADT 派生编解码器。

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

隐式作用域与优先级

只要手写的隐式编解码器处于作用域内,它的优先级就高于派生结果。请将它放入伴生对象中,以便始终能够找到它。

对于同一类型,请避免同时导入 auto._ 和手动实例,以免产生歧义。

快速检查

检验您对自定义编解码器的理解。

回顾

自定义编解码器可以处理与您的类型不匹配的 JSON。您可以使用 Decoder.instance、forProductN 构建它们,也可以通过 map、contramap 和 emap 转换现有编解码器。

枚举和日期是常见场景;使用 Codec.from 将两个方向的转换打包在一起,并使用判别字段处理 ADT。

常见问题解答

「自定义编解码器」课时是免费的吗?

是的 — 「自定义编解码器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。

「自定义编解码器」这节课中我会学到什么?

处理复杂的数据形状。 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「自定义编解码器」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?

能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 解析 JSON
  2. 解码为样例类
  3. 编码为 JSON
  4. 自定义编解码器
← 返回 Scala for Backend Engineering & Functional Programming