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بايت → حرف