شرح JSON: الصياغة والقواعد والتعامل مع JSON في JavaScript وPython
JSON هو أبسط صيغ تبادل البيانات وأوسعها انتشارًا؛ وببضع قواعد قصيرة ودالتين في كل لغة، تستطيع قراءة أي بيانات وإنشاءها وتخزينها.
إن كنت تعمل مع واجهات API أو تطبيقات الجوال أو حتى ملف إعدادات لمشروع ما، فستصادف JSON عاجلًا أو آجلًا. JSON صيغة نصية لتخزين البيانات ونقلها، يستطيع البشر قراءتها وتستطيع كل لغات البرمجة معالجتها بسهولة. يبدأ هذا الشرح بالتعريف وقواعد الصياغة، ويمر على مثال حقيقي، ثم ينتقل إلى قراءة JSON وكتابته في JavaScript وPython، مع اهتمام خاص بالحروف المُعلَّمة والنصوص العربية وغيرها من النصوص غير الإنجليزية، ويتناول في الختام الأخطاء الشائعة وكيف تتتبّعها باستخدام منسّق JSON. كل شيفرة في هذه الصفحة جرى تشغيلها، والمخرجات المعروضة حقيقية.
ما هو JSON؟
JSON اختصار لـ JavaScript Object Notation، أي «صيغة كائنات JavaScript». صياغته مأخوذة من طريقة كتابة الكائنات في JavaScript، لكنه غير مرتبط بأي لغة: ففي Python وPHP وJava وC# وGo وكل لغة أخرى تقريبًا أدوات جاهزة للتعامل معه. JSON معرّف في المعيارين RFC 8259 وECMA-404، وملفاته تستخدم الامتداد .json، ونوع وسائطه على الويب application/json. ومن استخداماته الشائعة:
- واجهات الويب البرمجية (Web APIs): معظم الخدمات الحديثة ترسل طلباتها واستجاباتها بصيغة JSON.
- ملفات الإعدادات: مثل
package.jsonفي مشاريع Node.js أوcomposer.jsonفي PHP. - تخزين البيانات: في
localStorageداخل المتصفح، وفي أعمدة JSON بقواعد البيانات مثل PostgreSQL وMySQL، وفي قواعد البيانات المستندية.
قواعد صياغة JSON
يُبنى كل مستند JSON من ستة أنواع من القيم، يمكن أن يتداخل بعضها داخل بعض:
| النوع | مثال | الوصف |
|---|---|---|
| object (كائن) | {"city": "Lisbon"} | أزواج من المفاتيح والقيم داخل أقواس معقوفة |
| array (مصفوفة) | [1, 2, 3] | قائمة مرتّبة من القيم داخل أقواس مربعة |
| string (سلسلة نصية) | "hello" | نص داخل علامتي اقتباس مزدوجتين |
| number (رقم) | 42، -3.5، 1.2e6 | عدد صحيح أو عشري، دون علامات اقتباس |
| boolean (قيمة منطقية) | true، false | بأحرف صغيرة فقط |
| null | null | غياب القيمة |
القواعد التي تقف وراء معظم الأخطاء:
- المفاتيح دائمًا سلاسل نصية داخل علامتي اقتباس مزدوجتين:
{"name": "Ali"}صحيح، و{name: "Ali"}خاطئ. - علامات الاقتباس المزدوجة وحدها مسموحة؛ فـ
'Ali'بين علامتي اقتباس مفردتين ليس JSON صالحًا. - الفواصل الزائدة في النهاية ممنوعة: لا يجوز أن تأتي فاصلة بعد آخر عنصر في كائن أو مصفوفة.
- لا توجد تعليقات: لا
//ولا/* */. - القيم الخاصة بلغة معيّنة غير مسموحة:
undefinedوNaNوInfinityوTrueوNoneليست JSON صالحًا. - لا يجوز أن تبدأ الأرقام بصفر:
012غير صالح. ولهذا ينبغي أن تُكتب أرقام الهواتف والرموز البريدية والمعرّفات المشابهة سلاسلَ نصية، مثل"0123456789"؛ وبذلك يُحفظ الصفر في البداية أيضًا. - المحارف الخاصة داخل السلاسل النصية تُهرَّب:
\"لعلامة الاقتباس المزدوجة، و\\للشرطة المائلة العكسية، و\nللسطر الجديد، و\uXXXXلأي محرف Unicode. - المسافات وفواصل الأسطر بين الأجزاء لا تهم؛ ولهذا يمكن كتابة JSON مضغوطًا في سطر واحد، أو بمسافات بادئة مرتّبة.
مثال حقيقي: طلب من متجر إلكتروني
{
"orderId": 10245,
"createdAt": "2026-09-17T10:30:00Z",
"customer": {
"name": "Maria",
"phone": "0123456789",
"city": "Lisbon"
},
"items": [
{ "title": "Python for Beginners", "price": 32, "qty": 1 },
{ "title": "Wireless Mouse", "price": 45, "qty": 2 }
],
"paid": true,
"discountCode": null
}
يعرض هذا المثال كل أنواع القيم. فالحقل customer كائن متداخل، وitems مصفوفة من الكائنات. الأسعار أرقام، أما رقم الهاتف فسلسلة نصية حتى يُحفظ الصفر في بدايته. ولا يوجد في JSON نوع «تاريخ»، لذا فإن وقت الطلب سلسلة نصية بصيغة ISO 8601 القياسية. ويستخدم discountCode القيمة null ليقول إن هذا الطلب لم يكن له رمز خصم. ولقراءة سعر العنصر الثاني تكتب order.items[1].price في JavaScript وorder["items"][1]["price"] في Python.
التعامل مع JSON في JavaScript
في JavaScript دالتان مدمجتان تكفيان لمعظم المهام: JSON.parse تحوّل نص JSON إلى كائن، وJSON.stringify تحوّل الكائن إلى نص JSON.
قراءة JSON بالدالة JSON.parse
const text = '{"name": "Maria", "age": 28, "skills": ["JavaScript", "Python"], "active": true}';
const user = JSON.parse(text);
console.log(user.name); // Maria
console.log(user.skills[0]); // JavaScript
console.log(user.active); // true
إنشاء JSON بالدالة JSON.stringify
const product = { title: "Café Guide", price: 32, tags: ["travel", "bestseller"], discount: null };
console.log(JSON.stringify(product));
console.log(JSON.stringify(product, null, 2));
الاستدعاء الأول يعطي JSON مضغوطًا في سطر واحد، يناسب الإرسال عبر الشبكة، والثاني، مع 2 وسيطًا ثالثًا، يعطي مخرجات مقروءة بمسافة بادئة من مسافتين. أما الوسيط الثاني فمخصّص لتصفية القيم أو تحويلها، ويبقى عادةً null. المخرجات:
{"title":"Café Guide","price":32,"tags":["travel","bestseller"],"discount":null}
{
"title": "Café Guide",
"price": 32,
"tags": [
"travel",
"bestseller"
],
"discount": null
}
الحروف المُعلَّمة وغيرها من النصوص الخارجة عن ASCII، ومنها العربية، تبقى كما هي تمامًا في مخرجات JSON.stringify، ولا تتحوّل إلى تهريبات \u.
جلب JSON من API وحفظه في localStorage
async function loadProduct() {
const res = await fetch("https://api.example.com/products/1");
return await res.json();
}
localStorage.setItem("cart", JSON.stringify({ items: [101, 205] }));
const cart = JSON.parse(localStorage.getItem("cart"));
console.log(cart.items.length); // 2
التابع res.json() يحلّل الاستجابة بنفسه ويعيد كائنًا، فلا تمرّر نتيجته إلى JSON.parse مرة أخرى. وlocalStorage لا يخزّن إلا السلاسل النصية، لذا يُحوَّل الكائن بـ JSON.stringify عند حفظه وبـ JSON.parse عند قراءته.
قيم تغيّرها JSON.stringify
const data = { a: undefined, b: () => 1, c: NaN, d: new Date(Date.UTC(2026, 8, 17)) };
console.log(JSON.stringify(data));
// {"c":null,"d":"2026-09-17T00:00:00.000Z"}
المفاتيح التي قيمتها undefined أو دالة تُحذف بصمت، وNaN وInfinity تصبحان null، والتاريخ يصبح سلسلة ISO نصية. وحين تحلّل النتيجة من جديد يبقى التاريخ سلسلة نصية، ولا يعود إلى Date من تلقاء نفسه.
معالجة الأخطاء
try {
JSON.parse("{'name': 'Ali'}");
} catch (err) {
console.log(err.name); // SyntaxError
console.log(err.message); // Expected property name or '}' in JSON at position 1 (line 1 column 2)
}
أي نص غير صالح يطلق SyntaxError، لذا عالج البيانات القادمة من الخارج داخل try...catch. والرسالة أعلاه من مخرجات Node.js، وهي مطابقة لما يعرضه Chrome، أما Firefox وSafari فيصفان الخطأ نفسه بكلمات مختلفة.
التعامل مع JSON في Python
في Python تتولّى الوحدة القياسية json كل العمل، ولا حاجة إلى تثبيت أي شيء. ولها أربع دوال رئيسية: json.loads وjson.dumps تعملان مع السلاسل النصية، بينما تعمل json.load وjson.dump مباشرةً مع الملفات؛ وحرف s في الأوليين اختصار لكلمة string.
تحويل سلسلة JSON إلى قاموس
import json
text = '{"name": "Maria", "age": 28, "skills": ["JavaScript", "Python"], "active": true}'
user = json.loads(text)
print(user["name"]) # Maria
print(type(user)) # <class 'dict'>
print(user["active"]) # True
| JSON | Python | JavaScript |
|---|---|---|
| object | dict | Object |
| array | list | Array |
| string | str | String |
| number | int أو float | Number |
true / false | True / False | true / false |
null | None | null |
تحويل البيانات إلى JSON، ومشكلة النصوص الخارجة عن ASCII
product = {"title": "Café Guide", "price": 32, "tags": ["travel", "bestseller"], "discount": None}
print(json.dumps(product))
print(json.dumps(product, ensure_ascii=False, indent=2))
قد يفاجئك السطر الأول من المخرجات:
{"title": "Caf\u00e9 Guide", "price": 32, "tags": ["travel", "bestseller"], "discount": null}
تكتب json.dumps افتراضيًا كل محرف خارج ASCII على الشكل \uXXXX، فتصبح é على الشكل \u00e9، والنص العربي، أو المكتوب بأي أبجدية أخرى، يُهرَّب محرفًا محرفًا بالطريقة نفسها. هذه المخرجات صالحة، وكل برنامج يقرؤها بشكل صحيح، لكن البشر لا يستطيعون قراءتها، كما أنها تشغل مساحة أكبر. ومع ensure_ascii=False يُكتب النص كما هو تمامًا، وindent=2 ترتّب المخرجات تمامًا كما في مثال JavaScript.
قراءة ملفات JSON وكتابتها
with open("product.json", "w", encoding="utf-8") as f:
json.dump(product, f, ensure_ascii=False, indent=2)
with open("product.json", encoding="utf-8") as f:
loaded = json.load(f)
print(loaded["tags"][0]) # travel
نقطتان مهمتان: مرّر ensure_ascii=False إلى json.dump أيضًا، وافتح الملف دائمًا مع encoding="utf-8". ففي Windows، إن لم تحدّد الترميز، قد يلجأ Python إلى الترميز الافتراضي للنظام، وهو ليس UTF-8؛ والنتيجة إما خطأ UnicodeEncodeError وإما ملف مليء بالمحارف المشوّهة.
مخرجات print ليست JSON
print(product)
# {'title': 'Café Guide', 'price': 32, 'tags': ['travel', 'bestseller'], 'discount': None}
من الأخطاء الشائعة جدًا لصق مخرجات print لقاموس في مكان ما على أنها JSON. هذا النص هو تمثيل Python الخاص للبيانات: فيه علامات اقتباس مفردة، وNone بدلًا من null، لذا لن يقبله أي محلّل JSON. ولإنشاء JSON استخدم دائمًا json.dumps.
معالجة الأخطاء في Python
try:
json.loads("{'name': 'Ali'}")
except json.JSONDecodeError as e:
print(e) # Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
الاستثناء json.JSONDecodeError صنف فرعي من ValueError، وخصائصه lineno وcolno وpos تحدّد موضع الخطأ. وإن كنت تستخدم مكتبة requests، فإن response.json() تجري التحويل نفسه لاستجابة API. وهناك فرق دقيق: تقبل Python افتراضيًا NaN في مدخلاتها وتكتبها في مخرجاتها أيضًا، مع أن هذه القيمة ليست جزءًا من JSON القياسي؛ ولمنع ذلك مرّر allow_nan=False إلى json.dumps.
أخطاء JSON الشائعة وما تعنيه رسائلها
| الخطأ | مثال غير صالح | رسالة JavaScript (Node.js وChrome) | رسالة Python |
|---|---|---|---|
| علامات اقتباس مفردة أو مفتاح بلا علامات اقتباس | {'name': 'Ali'} | Expected property name or '}' | Expecting property name enclosed in double quotes |
| فاصلة زائدة في نهاية كائن | {"a": 1,} | Expected double-quoted property name | Python 3.14: Illegal trailing comma before end of object؛ Python 3.12: Expecting property name enclosed in double quotes |
| فاصلة زائدة في نهاية مصفوفة | [1, 2,] | Unexpected token ']' | Python 3.14: Illegal trailing comma before end of array؛ Python 3.12: Expecting value |
| فاصلة ناقصة، أو تعليق بعد قيمة | {"a": 1 "b": 2} | Expected ',' or '}' after property value | Expecting ',' delimiter |
| قيمة من Python أو JavaScript | {"a": True} | Unexpected token 'T' | Expecting value |
| رقم يبدأ بصفر | {"phone": 0123456789} | Unexpected number | Expecting ',' delimiter |
| سلسلة نصية غير مغلقة | {"a": "x} | Unterminated string | Unterminated string starting at |
| مدخلات فارغة | نص فارغ | Unexpected end of JSON input | Expecting value |
تذكر الرسائل عادةً الموضع أيضًا، مثل position 8 أو line 1 column 9، وكثيرًا ما تكون المشكلة الحقيقية قبل تلك النقطة مباشرةً. وإن بدأت رسالة JavaScript بـ Unexpected token '<'، فالأرجح أنك تلقيت صفحة HTML بدلًا من JSON، كصفحة خطأ 404 أو صفحة تسجيل دخول؛ ودليل خطأ Unexpected token يشرح هذه الحالة بالتفصيل. والملف المحفوظ مع BOM يعطي في Python الخطأ Unexpected UTF-8 BOM؛ فافتحه مع encoding="utf-8-sig" أو احفظه دون BOM.
التنسيق والتحقق بمنسّق JSON
حين يصلك مستند JSON طويل في سطر واحد، أو لا تستطيع معرفة سبب فشل تحليله، فإن منسّق JSON هو أسرع طريق. تعمل الأداة بمحلّل JSON الخاص بالمتصفح نفسه، ولا تغادر البيانات متصفحك أبدًا، لذا يمكنك أن تفحص بأمان استجابات API التي تحتوي على رموز مصادقة (tokens) أو بيانات مستخدمين. الخطوات:
- الصق JSON في مربع JSON المُدخل، فيُنسَّق فورًا؛ وزر تنسيق يفعل الشيء نفسه.
- اختر مقدار الإزاحة من قائمة المسافة البادئة: مسافتان أو 4 مسافات أو علامة جدولة.
- إن كان JSON غير صالح، تعرض رسالة تبدأ بـ
JSON غير صالح:نص الخطأ الصادر عن المتصفح، وهو يتضمن عادةً موضع المشكلة؛ استعن بالجدول أعلاه لفهم معناه وإصلاحه. - يعرض تبويب شجرة البيانات على شكل شجرة قابلة للطي، ويفتح زرا توسيع الكل وطي الكل كل الفروع أو يغلقانها؛ وهذا مفيد جدًا للعثور على مفتاح في استجابة كبيرة.
- ولإرسال البيانات أو تخزينها بشكل مضغوط، يزيل ضغط كل المسافات وفواصل الأسطر. ثم انسخ النتيجة بزر نسخ.
وحين يحتاج الكود الذي تكتبه إلى أنواع لبيانات JSON التي يستقبلها، يولّد تحويل JSON إلى أنواع تعريفات TypeScript أو Go أو Python من عينة؛ ويشرح دليل تحديد نوع استجابة API في TypeScript ذلك خطوة بخطوة.