0Pricing
Scala for Backend Engineering & Functional Programming · บทเรียน

ตัวเข้ารหัสและถอดรหัสแบบกำหนดเอง

จัดการรูปแบบข้อมูลที่ซับซ้อน

ตัวเข้ารหัสและถอดรหัสแบบกำหนดเอง เป็นบทเรียน Scala for Backend Engineering & Functional Programming ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Scala for Backend Engineering & Functional Programming และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 4 บทเรียน

เมื่อจำเป็นต้องใช้ Codec แบบกำหนดเอง

การสร้างจากการอนุมานครอบคลุมกรณีทั่วไป แต่บางครั้ง JSON ไม่ได้มีโครงสร้างเหมือน case class ของคุณ เช่น ชื่อฟิลด์แบบเดิม enum ที่เข้ารหัสไว้ หรือรูปแบบอย่างการประทับเวลา

ในกรณีนั้น คุณต้องเขียน Decoder หรือ Encoder หรือเขียนทั้งสองอย่างด้วยตนเอง

Decoder จาก Cursor

วิธีสร้าง decoder ที่ระบุรายละเอียดชัดเจนที่สุดคือใช้ 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 ด้วยตนเอง

เขียนให้สอดคล้องกับ decoder ด้วย 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))

การแปลง Codec ที่มีอยู่

โดยทั่วไปคุณไม่จำเป็นต้องเริ่มจากศูนย์ map ใช้ปรับผลลัพธ์ของ decoder และ contramap ใช้ปรับอินพุตของ encoder

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

Codec สำหรับ Enum

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 กลับ

จับคู่ decoder ของ enum กับ encoder ที่ใช้ contramap ซึ่งแปลงแต่ละ case ให้เป็นแท็กของตน

ตอนนี้ sealed trait จะไปกลับใน JSON ในรูปสตริงธรรมดาได้

import io.circe.Encoder

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

Codec แบบกำหนดเองสำหรับวันที่

Circe ไม่มี codec ของ java.time มาให้ในโมดูลหลัก ดังนั้นวันที่จึงเป็นกรณีตัวอย่างคลาสสิกสำหรับ 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[A] เดียวด้วย Codec.from(decoder, encoder)

วิธีนี้ทำให้มี implicit เพียงตัวเดียวอยู่ในขอบเขต แทนที่จะมีสองตัวแยกกัน

import io.circe.Codec

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

Discriminator สำหรับ ADT

สำหรับลำดับชั้นแบบ sealed ที่มีข้อมูล circe-generic-extras จะเพิ่มฟิลด์ discriminator แทนออบเจ็กต์ตัวห่อ

กำหนดค่าเพียงครั้งเดียว แล้วสร้าง codec สำหรับ 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

ขอบเขตและลำดับความสำคัญของ Implicit

implicit codec ที่เขียนเองจะมีลำดับความสำคัญเหนือการสร้างจากการอนุมาน ตราบใดที่มันอยู่ในขอบเขต ให้ใส่ไว้ใน companion object เพื่อให้ค้นพบได้เสมอ

หลีกเลี่ยงการนำเข้าทั้ง auto._ และ instance ที่เขียนเองสำหรับชนิดเดียวกัน เพื่อป้องกันความกำกวม

ตรวจสอบความเข้าใจอย่างรวดเร็ว

ทดสอบความเข้าใจของคุณเกี่ยวกับ codec แบบกำหนดเอง

สรุปทบทวน

codec แบบกำหนดเองใช้จัดการ JSON ที่ไม่ตรงกับชนิดข้อมูลของคุณ สร้างได้ด้วย Decoder.instance, forProductN หรือแปลง codec ที่มีอยู่ด้วย map, contramap และ emap

Enum และวันที่เป็นกรณีที่พบบ่อย ให้รวมสองทิศทางด้วย Codec.from และใช้ discriminator สำหรับ ADT

คำถามที่พบบ่อย

บทเรียน “ตัวเข้ารหัสและถอดรหัสแบบกำหนดเอง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ตัวเข้ารหัสและถอดรหัสแบบกำหนดเอง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Scala for Backend Engineering & Functional Programming ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ตัวเข้ารหัสและถอดรหัสแบบกำหนดเอง”

จัดการรูปแบบข้อมูลที่ซับซ้อน คุณปฏิบัติ Scala for Backend Engineering & Functional Programming ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Scala for Backend Engineering & Functional Programming หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Scala for Backend Engineering & Functional Programming บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 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