Skip to content

التسلسل: "ترجمة" البيانات

🎯 السؤال الأساسي

كيف تنتقل البيانات عبر الشبكة؟ هذا مثل السؤال: كلام شخص، كيف يجعله شخص آخر يفهم؟ التسلسل يحل مشكلة "ترجمة البيانات" - تحويل الكائنات في الذاكرة إلى تنسيق يمكن نقله.


ضرورة تسلسل البيانات

في عملية التفاعل بين الواجهة الأمامية والخلفية، تحتاج البيانات إلى المرور بعدة "تحولات" لتنتقل من الخادم إلى العميل.

السيناريو الأول: البيانات التي تصل للواجهة الأمامية "تغيرت"

javascript
// الخلفية ترسل
Date birth = new Date(1990, 5, 15)

// الواجهة الأمامية تستقبل
{ "birth": "1990-06-15T00:00:00Z" }  // نص!

الواجهة الأمامية تريد استخدام .getFullYear()، لكنها تحصل على خطأ - لأن هذا ليس كائن Date، بل نص.

السيناريو الثاني: تشوه الأحرف الصينية

json
// المتوقع
{ "name": "张三" }

// المستلم فعليًا
{ "name": "å¼ ä¸" }

مشكلة ترميز الأحرف تؤدي إلى تشوه النص الصيني.

السيناريو الثالث: عنق زجاجة الأداء

json
// استجابة تحتوي على 10000 منتج
{
  "products": [
    { "id": 1, "name": "...", "description": "...", ... },
    // ... 9999 المزيد
  ]
}
// الحجم: 5.2 MB، وقت النقل: 3.5 ثانية

تكرار تنسيق JSON يؤدي إلى حزمة بيانات كبيرة جدًا، مما يؤثر بشدة على الأداء.


التسلسل مثل "الترجمة" - "ترجمة" كائنات الذاكرة إلى تنسيق يمكن نقله، والطرف المستقبل "يترجمها" مرة أخرى.


1. ما هو التسلسل/إلغاء التسلسل؟

التسلسل (Serialization) هو عملية تحويل الكائنات إلى تنسيق قابل للنقل.

إلغاء التسلسل (Deserialization) هو عملية استعادة التنسيق المنقول إلى كائن.

1.1 التشبيه بإرسال طرد

إرسال طردالتسلسلالوصف
تغليف العناصرتسلسلوضع العناصر في صندوق، لصق الملصق
النقلنقل الشبكةشاحنة البريد توصل للوجهة
فتح الطرد واستلام العناصرإلغاء التسلسلالمستلم يفتح الصندوق، يخرج العناصر

1.2 لماذا نحتاج التسلسل؟

السببالوصفمثال
نقل الشبكةالشبكة تنقل فقط تدفقات البايتاستدعاء API، اتصال RPC
تخزين دائمالقرص يخزن فقط بايتاتحفظ الكائنات في ملف، قاعدة بيانات
عبر اللغاتهياكل البيانات تختلف بين اللغاتكائن Java → قاموس Python
تخزين مؤقت موزعRedis/Memcached تخزن بايتاتتخزين معلومات المستخدم مؤقتًا

2. تنسيقات التسلسل الشائعة

👇 جرب بنفسك: انقر على الزر أدناه، ولاحظ عملية التسلسل في لغات مختلفة:

🔄Serialization Demo
📦In-memory object
const user = {
  id: 123,
  name: "Alice",
  email: "alice@example.com",
  age: 28
};
An object in memory, usable only by the current process
Serialize
{}JSON string68 bytes
{
  "id": 123,
  "name": "Alice",
  "email": "alice@example.com",
  "age": 28
}
Can be sent over the network and used across languages
Transfer
💻Binary52 bytes
Hex encoding (MessagePack):
\xa7 id 7b
\xa4 name \xa5 Alice
\xa5 email \xb1 alice@example.com
\xa3 age 1c
Protobuf/MessagePack, smaller and faster
📊 Format comparison
Format
Size
Speed
Readability
Cross-language
JSON
★★★☆☆
★★★☆☆
★★★★★
★★★★★
XML
★★☆☆☆
★★☆☆☆
★★★★★
★★★★★
Protobuf
★★★★★
★★★★★
★☆☆☆☆
★★★★☆
MessagePack
★★★★☆
★★★★☆
★★☆☆☆
★★★★★

2.1 JSON: الأكثر عمومية

المزايا:

  • قابلية قراءة جيدة، سهولة التصحيح
  • جميع اللغات تدعمه
  • المتصفح يدعمه أصلاً (JSON.parse / JSON.stringify)

العيوب:

  • حجم كبير (يحتوي الكثير من علامات {} "")
  • لا يدعم أنواع البيانات الغنية (Date، Map، Set تتحول إلى نصوص)

السيناريوهات المناسبة:

  • API العامة
  • التواصل بين الواجهة الأمامية والخلفية
  • ملفات التكوين

2.2 XML: كان سائدًا سابقًا

xml
<?xml version="1.0" encoding="UTF-8"?>
<user>
  <id>123</id>
  <name>张三</name>
  <email>zhangsan@example.com</email>
  <age>28</age>
</user>

المزايا:

  • هيكل واضح، يدعم التعليقات
  • يدعم الهياكل المتداخلة المعقدة
  • يوجد تحقق Schema (XSD)

العيوب:

  • حجم كبير، تحليل بطيء
  • تكرار الوسوم (<open></close>)

السيناريوهات المناسبة:

  • ملفات التكوين (Spring، MyBatis)
  • بروتوكول SOAP
  • تبادل بيانات معقدة

2.3 Protobuf: الأكثر كفاءة

protobuf
// user.proto
syntax = "proto3";
message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
  int32 age = 4;
}

المزايا:

  • حجم صغير (أصغر من JSON بـ 30-50%)
  • سرعة عالية (تحليل أسرع 5-10 مرات)
  • توافق مع الإصدارات السابقة (إضافة حقول جديدة لا تؤثر على الإصدارات القديمة)

العيوب:

  • غير قابل للقراءة (تنسيق ثنائي)
  • يحتاج ملف .proto للتعريف
  • لا يدعم الأنواع الديناميكية

السيناريوهات المناسبة:

  • اتصال الخدمات المصغرة الداخلي
  • سيناريوهات عالية الأداء (ألعاب، اتصال فوري)
  • تطبيقات الجوال (توفير البيانات)

2.4 MessagePack: توازن بين القراءة والأداء

json
// MessagePack هو نسخة ثنائية من JSON
// نفس البيانات، MessagePack أصغر من JSON بحوالي 30%

المزايا:

  • أصغر من JSON، أسرع من JSON
  • يحافظ على نموذج بيانات JSON
  • يدعم جميع أنواع JSON

العيوب:

  • غير قابل للقراءة
  • ليس بنفس كفاءة Protobuf

السيناريوهات المناسبة:

  • تحتاج أداء لكن لا تريد استخدام Protobuf
  • تخزين Redis المؤقت
  • رسائل WebSocket

3. مقارنة طرق التسلسل في اللغات المختلفة

اللغةمكتبة JSONمكتبة Protobufمكتبة XML
JavaScriptJSON.stringify()protobuf.jsfast-xml-parser
Pythonjson.dumps()protobufxmltodict
JavaJackson / Gsonprotobuf-javaJAXB
Goencoding/jsonprotoencoding/xml
C++nlohmann/jsonprotobuftinyxml2
C#System.Text.JsonGoogle.ProtobufSystem.Xml

💡 نصيحة الاختيار

  • التواصل بين الواجهة الأمامية والخلفية: JSON (سهولة التصحيح)
  • داخل الخدمات المصغرة: Protobuf (أداء مثالي)
  • ملفات التكوين: JSON أو YAML
  • التواصل مع أنظمة قديمة: XML (قد لا يكون هناك خيار آخر)

4. مقارنة الأداء

4.1 مقارنة الحجم (باستخدام كائن مستخدم كمثال)

التنسيقالحجمنسبة إلى JSON
JSON68 bytes100%
XML142 bytes209%
Protobuf38 bytes56%
MessagePack52 bytes76%

4.2 مقارنة السرعة (تسلسل 10000 مرة)

التنسيقالوقتنسبة إلى JSON
JSON45 ms100%
XML120 ms267%
Protobuf8 ms18%
MessagePack28 ms62%

💡 استنتاجات اختبار الأداء

  • Protobuf الأسرع: مناسب للسيناريوهات عالية الأداء
  • MessagePack الثاني: أسرع من JSON بحوالي 40%
  • JSON الأبطأ: لكنه كافٍ لمعظم السيناريوهات

5. المشكلات الشائعة

5.1 مشكلة تسلسل التاريخ

المشكلة: كائن Date بعد التسلسل يصبح نصًا

javascript
// قبل التسلسل
const date = new Date('2024-01-01')

// بعد التسلسل
JSON.stringify(date)  // "2024-01-01T00:00:00.000Z"

الحلول:

javascript
// الحل 1: التحويل إلى طابع زمني
{ createdAt: date.getTime() }  // 1704067200000

// الحل 2: التحويل إلى نص ISO
{ createdAt: date.toISOString() }  // "2024-01-01T00:00:00.000Z"

// الحل 3: تسلسل مخصص
JSON.stringify(obj, (key, value) => {
  if (value instanceof Date) {
    return { __type: 'Date', value: value.toISOString() }
  }
  return value
})

5.2 مشكلة المرجع الدائري

المشكلة: المرجع الدائري للكائن يسبب خطأ

javascript
const obj = { name: 'test' }
obj.self = obj
JSON.stringify(obj)  // TypeError: Converting circular structure to JSON

الحلول:

javascript
// الحل 1: تصفية المراجع الدائرية
const seen = new WeakSet()
JSON.stringify(obj, (key, value) => {
  if (typeof value === 'object' && value !== null) {
    if (seen.has(value)) return
    seen.add(value)
  }
  return value
})

// الحل 2: استخدام مكتبة flatted
import { parse, stringify } from 'flatted'
stringify(obj)  // معالجة تلقائية للمراجع الدائرية

5.3 مشكلة تشوه الأحرف الصينية

المشكلة: النص الصيني يتشوه بعد التسلسل

السبب:

  • عدم تطابق ترميز الأحرف (UTF-8 مقابل GBK)
  • علامة BOM

الحلول:

python
# Python ضمان استخدام UTF-8
import json
json.dumps(data, ensure_ascii=False)  # عدم ترميز الأحرف الصينية
javascript
// Node.js تعيين رأس الاستجابة
res.setHeader('Content-Type', 'application/json; charset=utf-8')

6. تطبيق عملي: حل تسلسل نظام التجارة الإلكترونية

6.1 تحليل السيناريو

السيناريواختيار التنسيقالسبب
App → API الخلفيةJSONسهولة التصحيح، توحيد الواجهة الأمامية والخلفية
الخلفية → الخلفية RPCProtobufأداء مثالي، توفير البيانات
تخزين Redis المؤقتMessagePackأصغر من JSON، يمكنه تسلسل كائنات معقدة
تسجيل السجلاتJSONسهولة تحليل أدوات تحليل السجلات

6.2 مثال كود

javascript
// استجابة API (JSON)
app.get('/api/products/:id', async (req, res) => {
  const product = await db.getProduct(req.params.id)
  res.json({
    code: 0,
    data: product
  })
})

// اتصال الخدمات المصغرة (Protobuf)
// product.proto
syntax = "proto3";
message Product {
  int32 id = 1;
  string name = 2;
  int32 price = 3;
}

// الخادم
const proto = require('./product.proto')
const message = proto.Product.create(product)
const buffer = proto.Product.encode(message).finish()

// العميل
const decoded = proto.Product.decode(buffer)

// تخزين Redis المؤقت (MessagePack)
const msgpack = require('msgpack-lite')
await redis.set(
  `product:${id}`,
  msgpack.encode(product)
)
const cached = msgpack.decode(await redis.get(`product:${id}`))

7. استخدام الذكاء الاصطناعي للمساعدة في اختيار حل التسلسل

يمكن للذكاء الاصطناعي مساعدتك في اختيار تنسيق التسلسل المناسب حسب السيناريو.

7.1 قالب التلميح

أنت مهندس معماري خبير في أنظمة البرمجيات، متقن لتقنيات تسلسل البيانات. الرجاء مساعدتي في اختيار حل التسلسل المناسب.

## سيناريو العمل
[صف سيناريو عملك، مثل: تطبيق تجارة إلكترونية، خادم ألعاب، خدمات مصغرة إلخ]

## المتطلبات التقنية
[اذكر القيود، مثل:
- فصل الواجهة الأمامية والخلفية (Vue + Node.js)
- متطلبات أداء عالية (QPS > 10000)
- حساسية البيانات (تطبيق جوال، يحتاج توفير البيانات)
- يحتاج عبر اللغات (Java + Python + Go)]

## خصائص البيانات
[صف خصائص البيانات، مثل:
- حجم بيانات كبير (استجابة واحدة > 1MB)
- هيكل معقد (تداخل متعدد المستويات)
- تحتوي أنواع خاصة مثل التاريخ والبيانات الثنائية]

## تنسيق الإخراج
الرجاء الإخراج بالتنسيق التالي:
1. الحل الموصى به (JSON/Protobuf/MessagePack/XML)
2. شرح الأسباب
3. مقارنة الأداء (تقدير الحجم والسرعة)
4. مثال كود

7.2 مثال عملي

إدخال التلميح:

أنت مهندس معماري خبير. تطبيق التجارة الإلكترونية لدينا يحتاج تحسين أداء واجهة قائمة المنتجات.

## سيناريو العمل
- واجهة قائمة منتجات تطبيق التجارة الإلكترونية
- استجابة واحدة تعيد 100 منتج
- كل منتج يحتوي: id, name, price, image, description إلخ 20 حقلاً
- حاليًا نستخدم JSON، حجم الاستجابة حوالي 200KB، الوقت 800ms

## المتطلبات التقنية
- الواجهة الأمامية Vue.js
- الخلفية Node.js + Go خدمات مصغرة
- يحتاج مراعاة الأداء وكفاءة التطوير

## أهداف التحسين
- تقليل وقت الاستجابة إلى أقل من 300ms
- تقليل حجم حزمة البيانات بنسبة 30%

إخراج الذكاء الاصطناعي:

الحل الموصى به: حل مختلط

1. **طبقة API**: الاستمرار في استخدام JSON (مناسب للواجهة الأمامية)
2. **اتصال الخدمات المصغرة**: التبديل إلى Protobuf (تحسين الأداء)
3. **البيانات الثابتة**: تفعيل ضغط gzip

### شرح الأسباب
- JSON الأكثر ملاءمة للواجهة الأمامية، لا يحتاج تحليل إضافي
- خدمات الخلفية المصغرة تستخدم Protobuf، يمكن تقليل حجم البيانات بنسبة 40%
- ضغط gzip فعال جدًا للنصوص (JSON يمكن ضغطه بنسبة 70%)

### مقارنة الأداء
| الحل | الحجم | الوقت |
|------|------|------|
| الحالي (JSON) | 200 KB | 800 ms |
| + gzip | 60 KB | 350 ms |
| + Protobuf | 50 KB | 280 ms |

### مثال كود
[كود التنفيذ المحدد...]

جدول المصطلحات

المصطلحالإنجليزيةالشرح
التسلسلSerializationكائن → تدفق بايتات
إلغاء التسلسلDeserializationتدفق بايتات → كائن
JSONJavaScript Object Notationتنسيق النص الأكثر استخدامًا
XMLExtensible Markup Languageلغة ترميز، كانت سائدة
ProtobufProtocol Buffersتنسيق فعال مفتوح المصدر من Google
MessagePack-النسخة الثنائية من JSON
الترميزEncodingحرف → بايت
فك الترميزDecodingبايت → حرف