המחלקה `JSON` ב-JavaScript
מאמר זה מסביר את המחלקה JSON ב-JavaScript.
נסביר את המחלקה JSON ב-JavaScript עם דוגמאות מעשיות.
YouTube Video
המחלקה JSON ב-JavaScript
לאובייקט JSON יש בעיקר שתי מתודות. JSON.stringify() ממיר אובייקט למחרוזת JSON, ו-JSON.parse() יוצר אובייקט ממחרוזת JSON. JSON הוא פורמט להחלפת נתונים שבו ניתן לייצג רק חלק מהערכים של JavaScript. פונקציות, 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מופעל עבור כל מפתח, והערך המוחזר משמש באובייקט הסופי. יש לבצע בדיקה קפדנית של פורמט תאריך בהתאם לאפליקציה שלך.
הדפסה נאה (Pretty-print) ופרמטר ה-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מהווה פתרון עמיד.
שיקולי ביצועים
סיריאליזציה ודסיריאליזציה תכופות של אובייקטים גדולים מכבידות על ה-CPU והזיכרון. במידת הצורך, שקול לשלוח רק את ההבדלים, להשתמש בפורמטים בינאריים (כמו MessagePack), או לבצע סטרימינג (עיבוד נתונים רציף). בסביבות דפדפן, לעיתים ניתן להשתמש ב-structuredClone (להעתקה) או באובייקטים ניתנים להעברה ב-postMessage.
המלצות ממוקדות (בקצרה)
ניתן גם לשקול את הנקודות הבאות:.
- בלוגים, העדף שלא להשתמש ב-pretty-print והשתמש ב-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 בערוץ היוטיוב שלנו. נא לבדוק גם את ערוץ היוטיוב.