فئة `JSON` في جافاسكريبت
تشرح هذه المقالة فئة JSON في جافاسكريبت۔
سنشرح فئة JSON في جافاسكريبت مع أمثلة عملية۔
YouTube Video
فئة JSON في جافاسكريبت
كائن JSON يحتوي بشكل أساسي على طريقتين۔ JSON.stringify() يحول الكائن إلى سلسلة JSON، وJSON.parse() ينشئ كائن من سلسلة JSON۔ JSON هو تنسيق لتبادل البيانات يمكنه التعبير فقط عن بعض قيم جافاسكريبت۔ الدوال، وundefined، وSymbol، والمرجعيات الدائرية لا يمكن تحويلها مباشرة إلى JSON۔
JSON.stringify()
أولاً، هنا مثال أساسي لتحويل كائن إلى سلسلة JSON۔
1// Convert a JavaScript object to a JSON string.
2const obj = { name: "Alice", age: 30, active: true };
3const jsonString = JSON.stringify(obj);
4console.log(jsonString); // {"name":"Alice","age":30,"active":true}
- هذا الكود هو أبسط مثال لتحويل كائن عادي إلى سلسلة JSON۔
JSON.stringifyتُرجع السلسلة بشكل متزامن۔
المعامل الثاني في JSON.stringify (replacer)
عند تحديد replacer كمعامل ثاني في JSON.stringify، يمكنك التحكم بدقة في قواعد التحويل۔ إليك مثال باستخدام مصفوفة للاحتفاظ بخصائص معينة فقط۔
1// Use a replacer array to include only specific properties during stringification.
2const user = { id: 1, name: "Bob", password: "secret", role: "admin" };
3const safeJson = JSON.stringify(user, ["id", "name", "role"]);
4console.log(safeJson); // {"id":1,"name":"Bob","role":"admin"}
- في هذا المثال، يتم استبعاد
passwordلضمان خروج السجلات بأمان۔ هذا مفيد عندما تريد إزالة المعلومات الحساسة لأسباب أمنية۔
استخدام replacer كدالة (لتحويل القيم والتصفية)
إذا كنت تريد مزيدًا من المرونة، يمكنك توفير دالة لمعالجة كل مفتاح وقيمة۔ فيما يلي مثال على الاحتفاظ بكائن Date كقيمة سلسلة ISO۔
1// Use a replacer function to customize serialization.
2const data = { name: "Carol", joined: new Date("2020-01-01T12:00:00Z") };
3const jsonCustom = JSON.stringify(data, (key, value) => {
4 // Convert Date objects to ISO strings explicitly
5 if (this && this[key] instanceof Date) {
6 return this[key].toISOString();
7 }
8 return value;
9});
10console.log(jsonCustom); // {"name":"Carol","joined":"2020-01-01T12:00:00.000Z"}
- عند استخدام
replacerمن نوع دالة، يتم تمرير المفتاح والقيمة، وتُستخدم القيمة المرجعة في الـ JSON النهائي۔thisتشير إلى الكائن الأب، لذلك يمكن استخدامها أيضًا في التحويلات المتداخلة۔
أساسيات JSON.parse()
JSON.parse يحول سلسلة JSON إلى كائن مجددًا۔ قم بتغليفها بـ try/catch حتى تتمكن من معالجة JSON غير الصالح۔
1// Parse a JSON string into an object and handle parsing errors.
2const jsonText = '{"title":"Example","count":42}';
3try {
4 const parsed = JSON.parse(jsonText);
5 console.log(parsed.title); // Example
6} catch (err) {
7 console.error("Invalid JSON:", err.message);
8}- إذا كانت السلسلة غير صالحة، سيتم رفع استثناء، لذا تأكد دائمًا من تضمين معالجة الاستثناءات عند التعامل مع مصادر خارجية۔
استعادة مخصصة باستخدام reviver (مثال على استعادة كائن Date)
باستخدام المعامل الثاني reviver في JSON.parse، يمكنك تنفيذ استعادة القيم۔ إليك مثالًا على تحويل تاريخ محفوظ كسلسلة إلى كائن Date۔
1// Use a reviver to turn ISO date strings back into Date objects.
2const jsonWithDate = '{"name":"Dana","joined":"2021-06-15T09:00:00.000Z"}';
3const obj = JSON.parse(jsonWithDate, (key, value) => {
4 // A simple check for ISO date-like strings
5 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
6 return new Date(value);
7 }
8 return value;
9});
10console.log(obj.joined instanceof Date); // true
- يتم استدعاء
reviverلكل مفتاح، وتُستخدم القيمة المرجعة في الكائن النهائي۔ يرجى إجراء تحقق صارم من صيغة التاريخ وفقًا لتطبيقك۔
الطباعة المنسقة ومعامل space
لإخراج منسق سهل القراءة، مرر رقمًا أو نصًا كمعامل ثالث إلى JSON.stringify۔
1// Pretty-print an object with 2-space indentation for readability.
2const config = { host: "localhost", port: 3000, debug: true };
3const pretty = JSON.stringify(config, null, 2);
4console.log(pretty);
5/* {
6 "host": "localhost",
7 "port": 3000,
8 "debug": true
9} */- بالنسبة لـ JSON الموجه للقراءة البشرية، كالسجلات أو ملفات الإعدادات، من المفيد تحديد المسافة البادئة۔ لكن يجب الانتباه أن ذلك يزيد من الحجم۔
كيفية التعامل مع المرجعيات الدائرية
JSON.stringify ترمي خطأ TypeError إذا كان هناك مرجعية دائرية۔ حل شائع هو إنشاء دالة replacer تستخدم مجموعة seen لتجنب المرجعيات الدائرية۔
1// Safely stringify objects that may contain circular references.
2function safeStringify(value) {
3 const seen = new WeakSet();
4 return JSON.stringify(value, (key, val) => {
5 if (val && typeof val === "object") {
6 if (seen.has(val)) return "[Circular]";
7 seen.add(val);
8 }
9 return val;
10 });
11}
12
13const a = { name: "A" };
14a.self = a;
15console.log(safeStringify(a)); // {"name":"A","self":"[Circular]"}
- عند استخدام
WeakSet، يمكنك اكتشاف المرجعيات الدائرية وتجنب تسرب الذاكرة في آن واحد۔ في حالة المرجعية الدائرية، سترجع القيمة"[Circular]"۔ من الممكن أيضًا تخصيص معرّف مرجعي بدلاً من"[Circular]"۔
تسلسل مخصص باستخدام طريقة toJSON
إذا قمت بتعريف دالة toJSON على كائن ما، فإن JSON.stringify يستخدم القيمة المرجعة منها۔ هذا مفيد عند الرغبة في تضمين قواعد التحويل لكل نوع۔
1// Define toJSON on a class to customize its JSON representation.
2class Point {
3 constructor(x, y) {
4 this.x = x;
5 this.y = y;
6 }
7 toJSON() {
8 // This will be used by JSON.stringify
9 return { x: this.x, y: this.y, type: "Point" };
10 }
11}
12
13const p = new Point(10, 20);
14console.log(JSON.stringify(p)); // {"x":10,"y":20,"type":"Point"}
- تتيح لك
toJSONتحديد قواعد التسلسل على مستوى الكائن، مما يجعلها أكثر محلية وأكثر وضوحًا من استخدامreplacer۔
قيود ومحاذير استخدام JSON
من الأمثلة البارزة التي لا يمكن تحويلها إلى JSON: القيم undefined، الدوال، Symbol، BigInt، والمرجعيات الدائرية۔ انتبه أيضًا لدقة القيم الرقمية (مثل الأعداد الصحيحة الكبيرة وتقريب الأعداد العشرية وفق IEEE)۔
1// Demonstrate values that can't be represented in JSON.
2const sample = {
3 a: undefined,
4 b: function () {},
5 c: Symbol("s"),
6 d: 9007199254740993n // BigInt (note: JSON.stringify will throw on BigInt)
7};
8// JSON.stringify will throw for BigInt and will drop undefined, functions, symbols in objects.
9try {
10 console.log(JSON.stringify(sample));
11} catch (err) {
12 console.error("Error during stringify:", err.message);
13}- عند التعامل مع
BigInt، قم بتحويلها إلى نص أو عرّف طريقتها الخاصة في التمثيل باستخدامreplacerمخصص أوtoJSON۔
الأمان (عند التعامل مع JSON غير موثوق)
JSON.parse بحد ذاته آمن ويعتبر أكثر أمانًا من eval، لكن من الخطر الوثوق بالكائن المحلل واستخدامه مباشرة للوصول إلى الخصائص أو استعلامات قاعدة البيانات۔ تحقق دائمًا وتأكد من أن البيانات تتوافق مع المخطط المتوقع۔
1// Parse external JSON and validate expected properties before use.
2const external = '{"username":"eve","role":"user"}';
3const parsed = JSON.parse(external);
4if (typeof parsed.username === "string" && ["user","admin"].includes(parsed.role)) {
5 console.log("Safe to use parsed.username and parsed.role.");
6} else {
7 throw new Error("Invalid payload");
8}- لتحقق صحة المدخلات (التحقق من المخطط)، فإن استخدام مكتبات مثل
ajvيعتبر حلاً قوياً۔
اعتبارات الأداء
التسلسل والفك المتكرر للكائنات الكبيرة يشكل عبئًا على المعالج والذاكرة۔ إذا لزم الأمر، يمكنك التفكير في إرسال الفروقات فقط، أو استخدام تنسيقات ثنائية مثل MessagePack، أو استخدام البث (معالجة البيانات بشكل متسلسل)۔ في بيئات المتصفح، يمكن الاستفادة من structuredClone (للنسخ) أو الكائنات القابلة للنقل في postMessage۔
نصائح محددة (موجزة)
يمكنك أيضًا أخذ النقاط التالية بعين الاعتبار:۔
- بالنسبة للسجلات، تجنب الطباعة المنسقة واستخدم JSON من سطر واحد لتقليل الحجم۔
- الكائنات التي يتم تسلسلها بشكل متكرر يجب أن تكون خفيفة الوزن مسبقًا، على سبيل المثال بحذف الخصائص غير الضرورية۔
- استخدم
replacerلتقليل التكرار واستبعاد الخصائص غير الضرورية۔
مثال عملي: تدفق إرسال/استقبال طلب واجهة برمجة التطبيقات (API)
أخيرًا، إليك تسلسلًا يوضح كيفية تحويل كائن بأمان إلى JSON لإرساله إلى الخادم واستعادة التاريخ من الاستجابة المستلمة۔
1// Prepare payload, stringify safely, send via fetch, and revive dates on response.
2async function sendUserUpdate(url, user) {
3 // Remove sensitive info before sending
4 const payload = JSON.stringify(user, (k, v) => (k === "password" ? undefined : v));
5 const res = await fetch(url, {
6 method: "POST",
7 headers: { "Content-Type": "application/json" },
8 body: payload
9 });
10
11 const text = await res.text();
12 // Reviver: convert ISO date strings back to Date
13 const data = JSON.parse(text, (key, value) => {
14 if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) {
15 return new Date(value);
16 }
17 return value;
18 });
19 return data;
20}- في هذا المثال، يتم استبعاد
passwordقبل الإرسال، ويتم استعادة التاريخ بواسطةreviverبعد الاستلام۔ في العمليات الفعلية، ستحتاج إلى إضافة معالجة للأخطاء والتعامل مع انتهاء المهلة۔
الملخص
باستخدام JSON.stringify() وJSON.parse()، يمكنك التحويل بين الكائنات والسلاسل النصية۔ يمكنك تخصيص عملية التحويل باستخدام replacer وreviver، وإذا قمت بتطبيق toJSON في الفئة، يمكنك أيضًا التحكم في كل كائن بشكل منفصل۔ المرجعيات الدائرية، وBigInt، والدوال، وما إلى ذلك لا يمكن التعامل معها كما هي، لذلك يجب معالجتها مسبقًا۔
تحقق دائمًا من صحة المدخلات الخارجية، وتأكد من عدم تضمين المعلومات الحساسة في السجلات أو الاتصالات۔ إذا كان حجم البيانات كبيرًا، فمن الأفضل تجنب الطباعة المنسقة، أو التفكير في الإرسال التفاضلي، أو استخدام التنسيقات الثنائية۔
يمكنك متابعة المقالة أعلاه باستخدام Visual Studio Code على قناتنا على YouTube.۔ يرجى التحقق من القناة على YouTube أيضًا.۔