برامج الترميز المخصصة
تعامَل مع البنى المعقدة
برامج الترميز المخصصة درس مجاني في Scala for Backend Engineering & Functional Programming على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في 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[A] واحدة باستخدام Codec.from(decoder, encoder).
وبذلك تحتفظ ضمن النطاق بتضمين واحد بدلًا من تضمينين منفصلين.
import io.circe.Codec
implicit val roleCodec: Codec[Role] =
Codec.from(dec, enc)المميّزات الخاصة بـ ADTs
بالنسبة إلى التسلسلات الهرمية المغلقة التي تحتوي على بيانات، تضيف 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، واستخدم المميّزات مع ADTs.
الأسئلة الشائعة
هل درس «برامج الترميز المخصصة» مجاني؟
نعم — نص درس «برامج الترميز المخصصة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Scala for Backend Engineering & Functional Programming، انتقل إلى CoddyKit PRO. تتضمن دورة Scala for Backend Engineering & Functional Programming 4 دروس في المجموع.
ماذا ستتعلم في «برامج الترميز المخصصة»؟
تعامَل مع البنى المعقدة تتمرن على Scala for Backend Engineering & Functional Programming مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 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 يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- تحليل JSON
- فك الترميز إلى Case Classes
- ترميز JSON
- برامج الترميز المخصصة