Kotlin: شرح التسلسل في كوتلن
آخر تحديث: 2026-08-26
تُولّد kotlinx.serialization أدوات التسلسل وقت الترجمة عبر إضافة مترجم — تحويل طلب Charlie ↔ JSON بدون انعكاس، وآمن الأنواع، وأسرع من Jackson. يعالج @SerialName تعيين الحقول في سطر واحد؛ وJson { ignoreUnknownKeys = true } يُهيّئ التسامح مع الأخطاء في سطر واحد.
1. ما ستتعلمه
- إضافة المترجم
@Serializableالمُولّدة لأدوات التسلسل وقت الترجمة - ترميز/فك ترميز JSON:
encodeToString/decodeFromString<T> - الحقول الاختيارية والقيم الافتراضية:
@SerialName/@Required - دعم التنسيقات المتعددة: JSON / ProtoBuf / CBOR / HOCON
- Charlie في الممارسة: طلب ↔ JSON + تعيين الحقول + تهيئة متسامحة مع الأخطاء
2. قصة مطور حقيقي
(1) المشكلة: أعطال وقت التشغيل في التسلسل القائم على الانعكاس
استخدم Bob وضع الانعكاس في Jackson لتسلسل كائنات Order. أثناء إعادة الهيكلة، تمت إعادة تسمية orderId إلى id — فشل إلغاء تسلسل JSON بصمت (عدم تطابق اسم الحقل)، وضاعت 500 سجل طلب.
(2) حل التسلسل وقت الترجمة
KOTLIN
// Jackson: قائم على الانعكاس، أخطاء وقت التشغيل
@JsonAlias("order_id") // سهل النسيان
data class Order(val orderId: String, ...)
// kotlinx.serialization: وقت الترجمة، أخطاء وقت الترجمة
@Serializable
data class Order(
@SerialName("order_id") val id: String, // المُترجم يتحقق!
val total: Double
)
توليد أدوات التسلسل وقت الترجمة — تصبح تغييرات أسماء الحقول أخطاء ترجمة، وليس فقدان بيانات وقت التشغيل.
3. @Serializable وإضافة المترجم
(1) تهيئة Gradle
KOTLIN
// build.gradle.kts
plugins {
kotlin("jvm") version "1.9.22"
kotlin("plugin.serialization") version "1.9.22" // إضافة مترجم التسلسل
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}
(2) التسلسل الأساسي
KOTLIN
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
@Serializable
data class Order(val id: String, val total: Double, val status: String)
// تسلسل: كائن -> سلسلة JSON
val order = Order("ORD-001", 299.99, "CONFIRMED")
val json = Json.encodeToString(order)
// {"id":"ORD-001","total":299.99,"status":"CONFIRMED"}
// إلغاء تسلسل: سلسلة JSON -> كائن
val decoded = Json.decodeFromString<Order>(json)
// Order(id=ORD-001, total=299.99, status=CONFIRMED)
(3) الانعكاس مقابل التسلسل وقت الترجمة
| البُعد | Jackson (انعكاس) | kotlinx.serialization |
|---|---|---|
| الآلية | انعكاس وقت التشغيل | توليد كود وقت الترجمة |
| الأمان | أخطاء وقت التشغيل | أخطاء وقت الترجمة |
| الأداء | أبطأ | أسرع (بدون عبء الانعكاس) |
| ProGuard | يتطلب قواعد حفظ | غير مطلوب |
| متعدد المنصات | JVM فقط | JVM / Native / JS |
4. ترميز وفك ترميز JSON
(1) تهيئة Json
KOTLIN
// الافتراضي: الوضع المتشدد
val strictJson = Json // يفشل مع المفاتيح غير المعروفة
// متساهل: تجاهل المفاتيح غير المعروفة (إصدارات API)
val lenientJson = Json {
ignoreUnknownKeys = true // تجاهل الحقول غير الموجودة في الفئة
isLenient = true // قبول JSON مشوّه
encodeDefaults = true // تضمين الحقول ذات القيم الافتراضية
prettyPrint = true // طباعة جميلة للإخراج
prettyPrintIndent = " " // المسافة البادئة
coerceInputValues = true // استخدام القيمة الافتراضية عند null لحقل non-null
}
val json = lenientJson.encodeToString(order)
(2) خيارات تهيئة Json
| الخيار | الافتراضي | الوصف |
|---|---|---|
ignoreUnknownKeys |
false | تجاهل حقول JSON غير الموجودة في الفئة |
isLenient |
false | تحليل متساهل (قبول JSON غير قياسي) |
encodeDefaults |
false | ترميز الحقول ذات القيم الافتراضية |
prettyPrint |
false | إخراج مُنسّق |
coerceInputValues |
false | استخدام القيمة الافتراضية عندما يستقبل حقل non-null قيمة null |
5. تعيين الحقول والقيم الافتراضية
(1) تعيين الحقول @SerialName
KOTLIN
@Serializable
data class Order(
@SerialName("order_id") val id: String, // JSON: order_id
@SerialName("order_total") val total: Double, // JSON: order_total
val status: String = "PENDING" // JSON: status (نفس الاسم)
)
// JSON: {"order_id":"ORD-001","order_total":299.99,"status":"CONFIRMED"}
(2) الحقول الاختيارية والقيم الافتراضية
KOTLIN
@Serializable
data class Order(
@SerialName("order_id") val id: String,
@SerialName("order_total") val total: Double,
val status: String = "PENDING", // اختياري بقيمة افتراضية
val customer: String? = null, // اختياري يقبل null
@SerialName("tax_rate") val taxRate: Double = 0.08
)
// الحقول الاختيارية المفقودة تستخدم القيم الافتراضية
val json = """{"order_id":"ORD-001","order_total":299.99}"""
val order = Json.decodeFromString<Order>(json)
// Order(id=ORD-001, total=299.99, status=PENDING, customer=null, taxRate=0.08)
(3) @Required لإجبار الحقول
KOTLIN
@Serializable
data class Order(
@Required val id: String, // يجب أن يكون موجودًا في JSON
@Required val total: Double // يجب أن يكون موجودًا في JSON
)
// غياب 'id' أو 'total' -> SerializationException
6. دعم التنسيقات المتعددة
KOTLIN
// ProtoBuf
import kotlinx.serialization.protobuf.ProtoBuf
val protoBytes = ProtoBuf.encodeToByteArray(order)
val fromProto = ProtoBuf.decodeFromByteArray<Order>(protoBytes)
// CBOR
import kotlinx.serialization.cbor.Cbor
val cborBytes = Cbor.encodeToByteArray(order)
// HOCON (تنسيق تهيئة)
import kotlinx.serialization.hocon.Hocon
(1) مقارنة التنسيقات
| التنسيق | قابلية القراءة | الحجم | السرعة | حالة الاستخدام |
|---|---|---|---|---|
| JSON | عالية | كبير | متوسطة | اتصالات API |
| ProtoBuf | منخفضة (ثنائي) | صغير | سريع | RPC عالي الأداء |
| CBOR | منخفضة (ثنائي) | متوسط | سريع | IoT / الأنظمة المدمجة |
| HOCON | عالية | متوسط | متوسطة | ملفات التهيئة |
7. تدفق ترميز/فك ترميز التسلسل
sequenceDiagram
participant Obj as Order Object
participant Ser as Serializer
participant JSON as JSON String
Note over Obj,Ser: ترميز
Obj->>Ser: @Serializable properties
Ser->>JSON: encodeToString()
Note over JSON: {"order_id":"ORD-001",...}
Note over Ser,Obj: فك الترميز
JSON->>Ser: decodeFromString<Order>(json)
Ser->>Obj: @Serializable constructor
Note over Obj: Order(id=ORD-001,...)
8. مثال كامل: تسلسل JSON لـ OrderProcessor
KOTLIN
// ============================================
// OrderProcessor - تسلسل JSON
// الميزة: طلب ↔ JSON مع تعيين الحقول
// ============================================
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class Address(
val street: String,
val city: String,
val country: String
)
@Serializable
data class OrderItem(
val sku: String,
val quantity: Int,
@SerialName("unit_price") val unitPrice: Double
) {
val subtotal: Double get() = quantity * unitPrice
}
@Serializable
data class Order(
@SerialName("order_id") val id: String,
@SerialName("order_total") val total: Double,
val status: String = "PENDING",
val customer: String? = null,
val items: List<OrderItem> = emptyList(),
val address: Address? = null,
@SerialName("tax_rate") val taxRate: Double = 0.08,
@SerialName("created_at") val createdAt: String = "2026-01-01T00:00:00Z"
)
val orderJson = Json {
ignoreUnknownKeys = true
encodeDefaults = true
prettyPrint = true
prettyPrintIndent = " "
}
fun main() {
// إنشاء طلب
val order = Order(
id = "ORD-001",
total = 299.99,
status = "CONFIRMED",
customer = "Alice",
items = listOf(
OrderItem("SKU-WIDGET", 3, 9.99),
OrderItem("SKU-GADGET", 1, 149.99)
),
address = Address("123 Main St", "New York", "US"),
createdAt = "2026-07-13T10:30:00Z"
)
// تسلسل: طلب -> JSON
println("=== تسلسل ===")
val jsonString = orderJson.encodeToString(order)
println(jsonString)
// إلغاء تسلسل: JSON -> طلب
println("\n=== إلغاء تسلسل ===")
val decoded = orderJson.decodeFromString<Order>(jsonString)
println("Order: ${decoded.id}, Total: \$${decoded.total} USD")
println("Items: ${decoded.items.map { "${it.sku} x${it.quantity}" }}")
// التعامل مع مفاتيح غير معروفة (إصدارات API)
println("\n=== إصدارات API ===")
val jsonWithExtraFields = """
{
"order_id": "ORD-002",
"order_total": 1500.00,
"status": "SHIPPED",
"unknown_field": "this is fine",
"new_api_version": 2
}
""".trimIndent()
val decodedWithExtra = orderJson.decodeFromString<Order>(jsonWithExtraFields)
println("Decoded with extra fields: ${decodedWithExtra.id}")
// JSON أدنى (الحقول المطلوبة فقط)
println("\n=== JSON أدنى ===")
val minimalJson = """{"order_id":"ORD-003","order_total":45.50}"""
val minimal = orderJson.decodeFromString<Order>(minimalJson)
println("Minimal: ${minimal.id}, Status: ${minimal.status}, Customer: ${minimal.customer}")
}
الإخراج:
TEXT
📖 للعرض فقط
=== تسلسل ===
{
"order_id": "ORD-001",
"order_total": 299.99,
"status": "CONFIRMED",
"customer": "Alice",
"items": [
{
"sku": "SKU-WIDGET",
"quantity": 3,
"unit_price": 9.99
},
{
"sku": "SKU-GADGET",
"quantity": 1,
"unit_price": 149.99
}
],
"address": {
"street": "123 Main St",
"city": "New York",
"country": "US"
},
"tax_rate": 0.08,
"created_at": "2026-07-13T10:30:00Z"
}
=== إلغاء تسلسل ===
Order: ORD-001, Total: $299.99 USD
Items: [SKU-WIDGET x3, SKU-GADGET x1]
=== إصدارات API ===
Decoded with extra fields: ORD-002
=== JSON أدنى ===
Minimal: ORD-003, Status: PENDING, Customer: null
9. أمثلة عملية سريعة
▶ مثال: @Serializable أساسي
KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class User(
val id: Int,
val name: String,
val email: String,
val active: Boolean = true
)
val user = User(1, "Alice", "alice@example.com")
// تسلسل إلى JSON
val json = Json.encodeToString(user)
println("JSON: $json")
// إلغاء التسلسل من JSON
val decoded = Json.decodeFromString<User>(json)
println("Decoded: $decoded")
// مع Pretty printing
val prettyJson = Json { prettyPrint = true }.encodeToString(user)
println("Pretty:\n$prettyJson")
**المخرجات:
TEXT
📖 للعرض فقط
JSON: {"id":1,"name":"Alice","email":"alice@example.com","active":true}
Decoded: User(id=1, name=Alice, email=alice@example.com, active=true)
Pretty:
{
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"active": true
}
▶ مثال: تسلسل متداخل وقوائم
KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class Address(
val street: String,
val city: String,
val country: String,
val zipCode: String? = null
)
@Serializable
data class Company(
val name: String,
val address: Address,
val employees: List<Employee> = emptyList(),
val tags: Set<String> = emptySet()
)
@Serializable
data class Employee(
val id: Int,
val name: String,
val role: String
)
val company = Company(
name = "Tech Corp",
address = Address("123 Main St", "NYC", "US"),
employees = listOf(
Employee(1, "Alice", "Engineer"),
Employee(2, "Bob", "Designer")
),
tags = setOf("tech", "startup")
)
val json = Json.encodeToString(company)
println("Company JSON:\n${Json { prettyPrint = true }.encodeToString(company)}")
// إلغاء التسلسل
val restored = Json.decodeFromString<Company>(json)
println("\nRestored: $restored")
**المخرجات:
TEXT
📖 للعرض فقط
Company JSON:
{
"name": "Tech Corp",
"address": {
"street": "123 Main St",
"city": "NYC",
"country": "US",
"zipCode": null
},
"employees": [
{"id": 1, "name": "Alice", "role": "Engineer"},
{"id": 2, "name": "Bob", "role": "Designer"}
],
"tags": ["tech", "startup"]
}
Restored: Company(name=Tech Corp, address=Address(street=123 Main St, city=NYC, country=US, zipCode=null), employees=[Employee(id=1, name=Alice, role=Engineer), Employee(id=2, name=Bob, role=Designer)], tags=[tech, startup])
▶ مثال: SerialName و default values
KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class ApiResponse(
@SerialName("status_code") val statusCode: Int,
@SerialName("data") val payload: String,
@SerialName("timestamp") val time: Long = System.currentTimeMillis()
)
val response = ApiResponse(200, "OK")
val json = Json.encodeToString(response)
println(json)
val decoded = Json.decodeFromString<ApiResponse>(json)
println("Decoded: $decoded")
// خصائص اختيارية مع قيم افتراضية
@Serializable
data class Config(
val name: String,
val maxConnections: Int = 100,
val timeout: Long = 30000,
val debug: Boolean = false
)
val minimalJson = """{"name":"MyApp"}"""
val config = Json.decodeFromString<Config>(minimalJson)
println("Config: $config")
// ignoreUnknownKeys - تجاهل الحقول الإضافية
val json1 = """{"name":"Test","unknownField":"ignored","maxConnections":50}"""
val config1 = Json { ignoreUnknownKeys = true }
.decodeFromString<Config>(json1)
println("With ignore: $config1")
**المخرجات:
TEXT
📖 للعرض فقط
{"status_code":200,"data":"OK","timestamp":1700000000000}
Decoded: ApiResponse(statusCode=200, payload=OK, time=1700000000000)
Config: Config(name=MyApp, maxConnections=100, timeout=30000, debug=false)
With ignore: Config(name=Test, maxConnections=50, timeout=30000, debug=false)
▶ مثال: sealed class للتسلسل
KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
sealed class Result {
abstract val message: String
@Serializable
data class Success(val data: String, override val message: String = "OK") : Result()
@Serializable
data class Failure(val errorCode: Int, override val message: String) : Result()
@Serializable
data class Loading(val progress: Int = 0, override val message: String = "Loading...") : Result()
}
val results: List<Result> = listOf(
Result.Success("User loaded"),
Result.Failure(404, "Not found"),
Result.Loading(50)
)
val json = Json.encodeToString(results)
println("Sealed class JSON:\n${Json { prettyPrint = true }.encodeToString(results)}")
val restored: List<Result> = Json.decodeFromString(json)
println("\nRestored: $restored")
**المخرجات:
TEXT
📖 للعرض فقط
Sealed class JSON:
[
{"type": "Result.Success", "data": "User loaded", "message": "OK"},
{"type": "Result.Failure", "errorCode": 404, "message": "Not found"},
{"type": "Result.Loading", "progress": 50, "message": "Loading..."}
]
Restored: [Success(data=User loaded, message=OK), Failure(errorCode=404, message=Not found), Loading(progress=50, message=Loading...)]
▶ مثال: Enums و maps
KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
enum class Priority {
LOW, MEDIUM, HIGH, CRITICAL
}
@Serializable
data class Task(
val id: String,
val title: String,
val priority: Priority,
val tags: List<String> = emptyList(),
val metadata: Map<String, String> = emptyMap()
)
val task = Task(
id = "T001",
title = "Deploy to production",
priority = Priority.CRITICAL,
tags = listOf("deploy", "urgent"),
metadata = mapOf("env" to "prod", "version" to "1.0.0")
)
val json = Json.encodeToString(task)
println(json)
val restored = Json.decodeFromString<Task>(json)
println("Restored: $restored")
println("Priority: ${restored.priority}")
**المخرجات:
TEXT
📖 للعرض فقط
{"id":"T001","title":"Deploy to production","priority":"CRITICAL","tags":["deploy","urgent"],"metadata":{"env":"prod","version":"1.0.0"}}
Restored: Task(id=T001, title=Deploy to production, priority=CRITICAL, tags=[deploy, urgent], metadata={env=prod, version=1.0.0})
Priority: CRITICAL
▶ مثال: تحويلات مخصصة (Custom Serializers)
KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*
// نوع DateTime مخصص
@Serializable(with = LocalDateTimeSerializer::class)
data class LocalDateTime(val iso: String) {
fun toReadable(): String = iso.replace("T", " ").substring(0, 16)
}
object LocalDateTimeSerializer : KSerializer<LocalDateTime> {
override val descriptor = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: LocalDateTime) {
encoder.encodeString(value.iso)
}
override fun deserialize(decoder: Decoder): LocalDateTime {
return LocalDateTime(decoder.decodeString())
}
}
val event = LocalDateTime("2026-07-27T15:30:00")
val json = Json.encodeToString(event)
println("JSON: $json")
val restored = Json.decodeFromString<LocalDateTime>(json)
println("Readable: ${restored.toReadable()}")
**المخرجات:
TEXT
📖 للعرض فقط
JSON: "2026-07-27T15:30:00"
Readable: 2026-07-27 15:30
▶ مثال: Generic types
KOTLIN
import kotlinx.serialization.*
import kotlinx.serialization.json.*
// فئة عامة قابلة للتسلسل
@Serializable
data class Container<T>(
val data: T,
val timestamp: Long = System.currentTimeMillis()
)
// استخدام مع أنواع مختلفة
val stringContainer = Container("Hello", 1700000000000)
val intContainer = Container(42, 1700000000001)
val json1 = Json.encodeToString(stringContainer)
val json2 = Json.encodeToString(intContainer)
println("String container: $json1")
println("Int container: $json2")
// إلغاء التسلسل - يجب تحديد النوع
val restored1: Container<String> = Json.decodeFromString(json1)
val restored2: Container<Int> = Json.decodeFromString(json2)
println("Restored 1: $restored1")
println("Restored 2: $restored2")
// List قابل للتسلسل
val orders = listOf(
Container("Order 1"),
Container("Order 2"),
Container("Order 3")
)
val listJson = Json.encodeToString(orders)
println("\nList: $listJson")
**المخرجات:
TEXT
📖 للعرض فقط
String container: {"data":"Hello","timestamp":1700000000000}
Int container: {"data":42,"timestamp":1700000000001}
Restored 1: Container(data=Hello, timestamp=1700000000000)
Restored 2: Container(data=42, timestamp=1700000000001)
List: [{"data":"Order 1","timestamp":1700000000017},{"data":"Order 2","timestamp":1700000000017},{"data":"Order 3","timestamp":1700000000017}]
❓ أسئلة شائعة
س هل يمكن استخدام kotlinx.serialization جنباً إلى جنب مع Jackson؟
ج نعم، لكن لا يُنصح بذلك. تختلف آليتا التسلسل. أثناء الانتقال، يمكنك استخدام
@JsonAlias كجسر؛ للمشاريع الجديدة، انتقل مباشرة إلى kotlinx.serialization.س هل يمكن تسلسل الفئات المختومة (sealed classes)؟
ج نعم. الفئات المختومة
@Serializable تتضمن تلقائيًا مميز نوع ("type":"SubClassName")، ويتم اختيار الفئة الفرعية الصحيحة أثناء إلغاء التسلسل.س كيف أخصص منطق التسلسل؟
ج حدد أداة تسلسل مخصصة باستخدام
@Serializable(with = CustomSerializer::class)، أو نفّذ واجهة KSerializer<T> للتحكم يدويًا في الترميز/فك الترميز.س هل يدعم التسلسل الأنواع العامة (generics)؟
ج نعم، لكنك تحتاج إلى توضيح الفئة العامة بـ
@Serializable. يتطلب decodeFromString معلمات نوع صريحة.س لماذا هناك حاجة لإضافة المترجم؟
ج إضافة مترجم كوتلن تُولّد تلقائيًا كود أداة التسلسل للفئات
@Serializable وقت الترجمة، متجنبة الانعكاس وقت التشغيل. هذا هو أساس أدائها وأمانها.س ما الفرق بين @SerialName و @JsonProperty؟
ج وظيفيًا متشابهان (تعيين اسم حقل JSON)، لكن @SerialName هو توضيح kotlinx.serialization المُعالج وقت الترجمة؛ و@JsonProperty هو توضيح Jackson المُعالج وقت التشغيل عبر الانعكاس.
📖 ملخص
@Serializable+ إضافة المترجم تُولّد أدوات التسلسل تلقائيًا — بدون انعكاسJson.encodeToString/Json.decodeFromString<T>للترميز وفك الترميز@SerialNameيعيّن أسماء حقول JSON، مفصولة عن أسماء خصائص كوتلن- القيم الافتراضية = حقول اختيارية؛
@Requiredيُجبر وجود الحقل ignoreUnknownKeys = trueيُفعل توافق إصدارات API- دعم التنسيقات المتعددة: JSON / ProtoBuf / CBOR / HOCON
📝 تمارين
- مبتدئ (⭐): أضف
@SerializableإلىOrder، سلسّل إلى JSON، وألغِ التسلسل. تلميح:@Serializable data class Order(...) - متوسط (⭐⭐): استخدم
@SerialNameلتعيين أسماء حقول Order (order_id،order_total)، وتهيئةJson { ignoreUnknownKeys = true }لتطور API. تلميح: راجع القسم 5 - متقدم (⭐⭐⭐): نفّذ أداة تسلسل مخصصة تسطّح عناصر
Orderفي تنسيق JSON مسطح (توسيع العناصر إلى تنسيقitem_1_sku،item_1_qty). تلميح: نفّذKSerializer<Order>