إلغاء التسلسل هو عملية استعادة كائن من تدفق بيانات JSON أو XML أو Protobuf، وهي ضرورية لأي تطبيق محمول يعمل مع API عن بُعد. وفقًا لـ Apple Developer (2026)، لا يزال التعامل غير الصحيح مع البيانات الواردة أحد الأسباب الشائعة للانهيارات على الأجهزة. JSONDecoder على iOS و Gson على Android هما أداتان قياسيتان، لكن لكل منهما ميزاته وقيوده الخاصة.
الخلاصة
إلغاء التسلسل هو عملية تحويل تدفق بايت أو نص منظم إلى كائن بلغة برمجة. في تطوير التطبيقات المحمولة، تحدث هذه العملية في كل مرة يتلقى فيها التطبيق ردًا من الخادم: سلسلة JSON تتحول إلى مثيل من فئة User أو Order أو Product. استقرار الشاشات التي تعرض البيانات للمستخدم يعتمد بشكل مباشر على صحة إلغاء التسلسل.
التسلسل وإلغاء التسلسل هما عمليتان متعاكستان، ونادرًا ما تكونان متماثلتين عمليًا. التسلسل يحول كائنًا إلى سلسلة لإرسالها إلى الخادم، بينما إلغاء التسلسل يستعيد الكائن من السلسلة المستلمة. قد يرسل الخادم حقلاً غير موجود في نموذج العميل، أو يستخدم تنسيق تاريخ مختلف، أو يعيد null بدلاً من رقم. وفقًا لـ Square Engineering (2025)، يتسبب عدم تماثل التنسيقات في 23% من أخطاء طبقة الشبكة في تطبيقات Android. لتقليل المخاطر، يتم استخدام إدارة إصدارات المخطط ومواصفات العقود الصارمة عبر OpenAPI.
JSON يظل التنسيق الأكثر شيوعًا لواجهات API المحمولة بفضل سهولة قراءته البشرية ودعمه المدمج. Protobuf من Google يُستخدم في الأنظمة عالية الحمل — فهو أصغر بنسبة 3-6 مرات من JSON ويتم تحليله بشكل أسرع، لكنه يتطلب توليد كود من ملفات .proto وغير قابل للقراءة بدون أدوات. XML أقل شيوعًا في التطبيقات المحمولة الحديثة، لكنه يُستخدم في خدمات SOAP للأنظمة المؤسسية وملفات تهيئة Android. MessagePack هو تنسيق ثنائي مشابه لـ JSON في الهيكل لكنه أكثر compact، شائع في أنظمة الوقت الفعلي.
تمر عملية إلغاء التسلسل بثلاث مراحل. أولاً، الترميز يقسم النص الخام إلى رموز: مفاتيح وسلاسل وأرقام وفواصل. ثم التحليل النحوي يتحقق من صحة الهيكل — هل الأقواس مغلقة، هل نوع علامات الاقتباس صحيح، هل التنسيق يتوافق مع RFC 8259. المرحلة النهائية هي التعيين إلى نموذج الكائنات للتطبيق، حيث يتم تعيين خاصية فئة لكل مفتاح JSON مع مراعاة استراتيجية التسمية.
ظهر نهجان للتعيين في تطوير التطبيقات المحمولة. Reflection (Gson, JSONSerialization) يحلل هيكل الفئة في وقت التشغيل عبر Java Reflection API أو Objective-C runtime — وهو مرن ولا يتطلب تهيئة إضافية، لكنه أبطأ ويستهلك ذاكرة أكثر. Code generation (Moshi codegen, kotlinx.serialization, Codable) يولد الكود في وقت الترجمة: أسرع وأكثر أمانًا من حيث الأنواع ولا يكشف الهيكل الداخلي عبر reflection. توصي JetBrains و Square بتوليد الكود لإصدارات الإنتاج — يصل تحسين الأداء إلى 2-4 مرات في معايير Google.
struct User: Codable {
let id: Int
let name: String
let email: String
let createdAt: Date
}
let json = """
{
"id": 42,
"name": "Alice",
"email": "alice@example.com",
"created_at": "2026-06-01T12:00:00Z"
}
"""
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let user = try decoder.decode(User.self, from: data)
مثال على إلغاء تسلسل JSON إلى نموذج User في Swift. استراتيجية convertFromSnakeCase تحول تلقائيًا مفاتيح API بصيغة snake_case إلى خصائص النموذج بصيغة camelCase — وهي ممارسة قياسية في مشاريع iOS. المعامل data هو البايتات الخام لاستجابة الخادم المستلمة عبر URLSession. معالجة الأخطاء عبر try تسمح بالتقاط JSON غير الصحيح دون تعطل التطبيق.
يدعم JSONDecoder أربع استراتيجيات للمفاتيح: useDefaultKeys (مطابقة تامة)، convertFromSnakeCase (snake_case → camelCase)، custom (closure) و convertFromKebabCase (kebab-case → camelCase). للتواريخ، تتوفر .iso8601 و .secondsSince1970 و .millisecondsSince1970 و dateFormatter مخصص. اختيار الاستراتيجية الصحيحة هو الخطوة الأولى نحو إلغاء تسلسل قوي، يمنع معظم أخطاء عدم تطابق التنسيقات.
JSONDecoder هو الآلية القياسية لإلغاء التسلسل في iOS SDK، ويعمل مع بروتوكول Codable. يقوم JSONDecoder تلقائيًا بتحليل JSON إلى مثيلات struct أو class، ويدعم الكائنات المتداخلة والمصفوفات والأنواع البدائية. للمنطق المخصص، يتم استخدام طريقة init(from: Decoder) — وهي تسمح بمعالجة التنسيقات غير القياسية والحقول المفقودة في إصدار قديم من API أو دمج عدة مفاتيح JSON في خاصية واحدة.
struct Order: Decodable {
let orderId: String
let amount: Double
let status: OrderStatus
enum OrderStatus: String, Decodable {
case pending, confirmed, shipped, cancelled
}
}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let order = try decoder.decode(Order.self, from: jsonData)
DateDecodingStrategy تحدد كيفية تفسير JSONDecoder لسلاسل التاريخ. .iso8601 هو الأكثر استخدامًا — التنسيق القياسي لواجهات REST API. التعداد المتداخل OrderStatus يتم فك ترميزه تلقائيًا من قيم السلاسل في JSON. هذا يتجنب الأرقام السحرية ويجعل الكود موثقًا ذاتيًا — حالة الطلب دائمًا لها مجموعة محددة بدقة من القيم.
منذ Swift 4.2، يدعم Codable property wrappers لإلغاء التسلسل المخصص للخصائص الفردية. @DefaultValue هو wrapper شائع يعين قيمة افتراضية إذا كان الحقل مفقودًا في JSON. @LosslessString يحول سلسلة إلى رقم والعكس. هذا مفيد بشكل خاص عندما يرسل الخادم id كسلسلة "123" لكن النموذج يتوقع Int. تقلل property wrappers من الكود المتكرر في init(from:) وتجعل النماذج أنظف.
على Android، يعتمد اختيار مكتبة إلغاء التسلسل على اللغة ومتطلبات المشروع. Gson من Google هو الخيار الأكثر شيوعًا، ويعمل عبر reflection لكن لديه مشاكل في الأداء مع التسلسلات الهرمية المعقدة. Moshi من Square يدعم كلاً من reflection و code generation، ويستهلك ذاكرة أقل ويعالج الاستجابات الكبيرة بشكل أسرع. kotlinx.serialization من JetBrains هو حل Kotlin أصلي مع تكامل في المترجم ولا يستخدم reflection على الإطلاق.
@Serializable
data class User(
@SerialName("user_id")
val userId: Int,
val name: String,
val email: String,
@SerialName("created_at")
val createdAt: String
)
val json = Json { ignoreUnknownKeys = true }
val user = json.decodeFromString<User>(response)
@Serializable هو تعليق توضيحي لمترجم Kotlin ينشط توليد الكود للفئة. المعامل ignoreUnknownKeys يمنع الانهيار إذا أرسل الخادم حقلاً مفقودًا في النموذج. لتعيين مفاتيح snake_case، يُستخدم @SerialName — وهو ما يعادل convertFromSnakeCase من iOS. وفقًا لـ JetBrains (2026)، تدعم المكتبة تعدد المنصات: نفس الفئة Serializable تعمل على Android و iOS (KMP) و Kotlin من جانب الخادم.
الاختيار بين المكتبات يعود إلى مفاضلة بين السرعة والمرونة. Gson جيد للنماذج الأولية ومشاريع Java — لا يتطلب تعليقات توضيحية ويعمل فورًا. Moshi يحتل موقعًا وسطًا: codegen عبر @JsonClass(generateAdapter = true) يعطي سرعة قريبة من kotlinx.serialization، بينما وضع reflection يوفر مرونة Gson. kotlinx.serialization هو الخيار الأسرع لمشاريع Kotlin النقية لكنه يتطلب Kotlin 1.4+ وإضافة Kotlin Serialization في Gradle.
| المكتبة | الآلية | السرعة | KMP |
|---|---|---|---|
| Gson | Reflection | منخفضة | لا |
| Moshi | Reflection / Codegen | متوسطة / عالية | لا |
| kotlinx.serialization | Codegen المترجم | عالية | نعم |
عدم تطابق النوع هو حالة يحتوي فيها JSON على قيمة من نوع بينما يتوقع النموذج نوعًا آخر. أرسل الخادم السلسلة "42" بدلاً من رقم، أو الرقم 1 بدلاً من true المنطقي. على iOS، سيرمي JSONDecoder خطأ DecodingError.typeMismatch افتراضيًا؛ على Android، سيحاول Gson التحويل، بينما يتطلب Moshi و kotlinx.serialization محولات صريحة. الحل هو استخدام استراتيجيات lenient أو أدوات إلغاء تسلسل مخصصة لحقول محددة.
عندما لا يتضمن الخادم حقلاً اختياريًا، ينهار الكود مع خطأ. الحقول Optional في Swift والأنواع القابلة للعدم في Kotlin تحل المشكلة: إذا كان الحقل null أو مفقودًا في JSON، تحصل الخاصية على nil/null ويستمر التطبيق في العمل. للحقول الإلزامية، من الجيد التحقق من وجودها على مستوى عميل API قبل إلغاء التسلسل. تتطلب Moshi و kotlinx.serialization جميع الحقول افتراضيًا — العلامات القابلة للعدم والقيم الافتراضية تزيل هذا القيد.
تغيير هيكل JSON على الخادم هو مصدر شائع لانهيارات الإنتاج. الممارسة القياسية هي إدارة إصدارات المخطط عبر حقل version في الكائن الجذر ودعم 2-3 إصدارات سابقة على العميل. kotlinx.serialization تسمح بتعريف نماذج متعددة لإصدارات مختلفة واختيار النموذج المناسب بناءً على حقل version بعد التحليل الأولي إلى JsonElement. الحماية الإضافية تشمل ignoreUnknownKeys للحقول الجديدة والقيم الافتراضية للحقول التي قد تُحذف.
| الخطأ | العرض | المكتبة مع الحماية |
|---|---|---|
| عدم تطابق النوع | DecodingError / استثناء | kotlinx — coerceInputValues = true |
| حقل مفقود | انهيار عند الوصول | Moshi — @Transient + default |
| تنسيق تاريخ غير صحيح | خطأ في فك الترميز | JSONDecoder — dateDecodingStrategy |
| حقول إضافية | تُتجاهل أو انهيار | kotlinx — ignoreUnknownKeys = true |
| Null في حقل غير قابل للعدم | انهيار في وقت التشغيل | Moshi — lenient مع @Nullable |
تسجيل أخطاء إلغاء التسلسل هو ممارسة إلزامية في الإنتاج. لف decode في do/catch، وسجل JSON الخام ونوع النموذج المتوقع في Crashlytics أو Sentry. هذا سيسمح بتحديد سريع لأي حقل من أي API تعطل وفي أي إصدار من التطبيق. بدون تسجيل، يبدو خطأ إلغاء التسلسل وكأنه انهيار غامض بدون سياق.
الأسئلة الشائعة
التحليل هو تفكيك النص المنظم إلى عناصر مكونة دون إنشاء نموذج مُنمّط بالضرورة. إلغاء التسلسل هو حالة خاصة من التحليل نتيجتها كائن لغة كامل مع أنواع خصائص معروفة. يمكن أن يكون التحليل تدفقيًا، بينما إلغاء التسلسل دائمًا ما ينشئ كائنًا كاملاً.
لمشروع بلغة Kotlin نقية، يُوصى بـ kotlinx.serialization — فهي مدمجة في المترجم، لا تستخدم reflection وتدعم Kotlin Multiplatform. لمشروع Java موجود — Moshi مع code generation. من الأفضل ترك Gson للمشاريع القديمة حيث يتطلب استبداله جهدًا كبيرًا.
على iOS، استخدم keyDecodingStrategy = .convertFromSnakeCase في JSONDecoder. على Android مع kotlinx.serialization، استخدم @SerialName لكل حقل. في Moshi، طبق @Json(name="field_name") أو JsonAdapter.Factory عام. النمط الموحد على مستوى المشروع هو أفضل ممارسة متفق عليها في عقد API.
السبب الأكثر شيوعًا هو null غير متوقع من الخادم على حقل مُعلن كإلزامي. في التطوير، يعيد الخادم بيانات كاملة؛ في الإنتاج، يعيد ردًا مختصرًا. الحل: وضع علامة قابلية للعدم (nullable) على جميع الحقول التي قد تكون مفقودة في Kotlin أو optional في Swift، واستخدام ignoreUnknownKeys والقيم الافتراضية.
Code generation (Moshi codegen, kotlinx.serialization, Codable) يعمل أسرع بـ 2-4 مرات من reflection في معايير Google. بالإضافة إلى السرعة، توليد الكود أكثر أمانًا من حيث الأنواع، ولا يتطلب بيانات وصفية للفئات في وقت التشغيل، ويتم اكتشاف أخطاء الأنواع في وقت الترجمة وليس أثناء إلغاء التسلسل.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا