Menu

JSON ב-JavaScript: JSON.stringify, JSON.parse ומלכודות נפוצות

איך ממירים אובייקטים של JavaScript ל-JSON ובחזרה: JSON.stringify, JSON.parse, ה-replacer וה-reviver, והערכים שלא שורדים את הדרך הלוך ושוב.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

JSON הוא טקסט, לא אובייקט

JSON (JavaScript Object Notation) הוא פורמט טקסט להעברת נתונים. הוא נראה כמו ליטרל אובייקט של JavaScript, אבל הוא מחרוזת: רצף תווים שאפשר לשלוח ברשת, לשמור בקובץ או להדביק בקובץ הגדרות.

// A JavaScript object - a live value in memory.
const user = { name: "Rosa", age: 30 };

// JSON - a string of text that represents the same data.
const json = '{"name":"Rosa","age":30}';

קל לבלבל ביניהם כי הם נראים דומה. מודל החשיבה שמסדר את הראש: אובייקטים חיים בתוך התוכנית שלכם, ו-JSON הוא איך שהם נראים כשהם יוצאים החוצה. ממירים ביניהם בעזרת שתי פונקציות: JSON.stringify (אובייקט → מחרוזת) ו-JSON.parse (מחרוזת → אובייקט).

JSON.stringify: מאובייקט למחרוזת

הופכים כל ערך של JavaScript לייצוג ה-JSON שלו:

התוצאה היא מחרוזת בשורה אחת בלי רווחים: קומפקטית ואידיאלית לשליחה ברשת. בדיקת ה-typeof מאשרת: string, לא object.

בשביל קריאוּת בזמן דיבוג, העבירו שני ארגומנטים נוספים. השני הוא replacer (נגיע אליו), והעברה של null פירושה "לכלול הכול". השלישי הוא ההזחה:

זו הצורה המקובלת להדפסה מעוצבת. השתמשו ב-2 או 4 רווחים, כמו שרוב הכלים מייצרים.

JSON.parse: ממחרוזת לאובייקט

הכיוון ההפוך: לוקחים מחרוזת JSON ומקבלים בחזרה ערך של JavaScript.

אחרי הפענוח עובדים עם אובייקט רגיל: גישה עם נקודה, גישה עם סוגריים מרובעים, מתודות מערך, כל הסיפור.

JSON.parse בררנית. כל אלה זורקים SyntaxError:

JSON.parse("{name: 'Rosa'}");      // unquoted key, single quotes
JSON.parse('{"name": "Rosa",}');   // trailing comma
JSON.parse("// a comment\n{}");    // comments aren't allowed
JSON.parse("");                    // empty string

בכל פעם שהקלט מגיע מ_מחוץ_ לתוכנית שלכם, כמו תשובה של fetch, קובץ או קלט של משתמש, עטפו את הפענוח ב-try/catch:

ערכים שלא שורדים את הדרך הלוך ושוב

JSON תומך בשישה סוגי ערכים: מחרוזות, מספרים, בוליאניים, null, מערכים ואובייקטים רגילים. כל השאר ב-JavaScript מושמט, משתנה או נשבר.

מה קורה:

  • פונקציות ו-undefined מוסרים בשקט מאובייקטים. בתוך מערכים הם הופכים ל-null, כי במערכי JSON אין חורים.
  • אובייקטי Date מומרים למחרוזת ISO דרך המתודה toJSON שלהם. הפענוח מחזיר את המחרוזת, לא Date.
  • BigInt זורק TypeError. למספרי JSON אין מקבילה לו.
  • גם Map, Set והפניות מעגליות לא עובדים ישירות.

הפתרון להעברת Date הלוך ושוב הוא פונקציית ה-reviver של JSON.parse:

ה-reviver רץ על כל זוג מפתח וערך ומאפשר לשנות ערכים בדרך פנימה.

ה-Replacer: סינון של מה שעובר המרה

הארגומנט השני של JSON.stringify מאפשר לשלוט במה שמגיע לפלט. העבירו מערך של מפתחות כדי לכלול רק אותם:

או העבירו פונקציה ללוגיקה חופשית: השמטת שדות, הסתרת ערכים, שינוי תוך כדי תנועה:

החזרת undefined מסירה את המפתח. החזרת כל ערך אחר מחליפה אותו.

התאמה אישית עם toJSON

אם לאובייקט יש מתודת toJSON, JSON.stringify קוראת לה וממירה את ערך ההחזרה במקום את האובייקט. כך Date שולט בפורמט שלו, ואפשר להשתמש באותו מנגנון:

מצוין למחלקות שרוצות צורה ציבורית עקבית, לא משנה מי ממיר אותן.

העתקה עמוקה (הטריק הישן, והטוב יותר)

במשך שנים, JSON.parse(JSON.stringify(obj)) היה השורה האחת להעתקה עמוקה של אובייקט רגיל:

זה עובד, כל עוד האובייקט מכיל רק ערכים שבטוחים ל-JSON. Dates, Maps ופונקציות שוברים את זה (ראו את בעיות הדרך הלוך ושוב שלמעלה).

ב-JavaScript מודרנית יש structuredClone, שמטפלת ב-Dates, Maps, Sets, typed arrays והפניות מעגליות:

השתמשו ב-structuredClone כשאפשר. שמרו את הטריק JSON.parse(JSON.stringify(...)) בכיס להעתקות מהירות של נתונים פשוטים.

דוגמה מהמציאות: הבאה ופענוח

המקום הנפוץ ביותר שבו תיגעו ב-JSON הוא HTTP. fetch לא מפענחת JSON בשבילכם: קוראים ל-.json() על התשובה (שבפועל היא JSON.parse על גוף התשובה):

שליחת JSON היא הכיוון ההפוך: JSON.stringify על הגוף והגדרת הכותרת Content-Type.

await fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Rosa", age: 30 }),
});

שתי התבניות האלה מכסות את רוב העבודה עם JSON בעולם האמיתי.

הבא בתור: Optional Chaining

ב-JSON שפוענח יש לעתים קרובות שדות אופציונליים: user.address.city שאולי לא קיים, response.data.items שעלול לחסור. גישה למאפיינים מקוננים לעומק בלי לקרוס היא בדיוק התפקיד של optional chaining (?.), וזה העמוד הבא.

שאלות נפוצות

מה ההבדל בין JSON לאובייקט של JavaScript?

JSON הוא פורמט טקסט: מחרוזת שנראית כמו ליטרל אובייקט של JavaScript אבל עם כללים נוקשים יותר. מפתחות חייבים להיות במירכאות כפולות, מחרוזות חייבות להשתמש במירכאות כפולות, והערכים מוגבלים למחרוזות, מספרים, בוליאניים, null, מערכים ואובייקטים רגילים. אובייקט של JavaScript הוא ערך חי בזיכרון שיכול להחזיק פונקציות, undefined, מופעים של Date וכל דבר אחר.

איך ממירים אובייקט של JavaScript ל-JSON?

קוראים ל-JSON.stringify(obj). היא עוברת על האובייקט ומחזירה מחרוזת JSON. העבירו ארגומנט שלישי, למשל JSON.stringify(obj, null, 2), כדי לקבל הדפסה מעוצבת עם הזחה של 2 רווחים. פונקציות וערכי undefined מושמטים מאובייקטים והופכים ל-null בתוך מערכים.

למה JSON.parse זורקת שגיאה?

JSON.parse קפדנית: פסיקים מיותרים בסוף, מירכאות בודדות, מפתחות בלי מירכאות והערות, כולם גורמים ל-SyntaxError. עטפו את הקריאה ב-try/catch בכל פעם שהקלט מגיע מבקשת רשת, מקובץ או ממשתמש, כלומר מכל מקום שבו המחרוזת עלולה לא להיות JSON תקין.

האם JSON.stringify שומרת על Dates?

לא. Date הופך למחרוזת ISO כמו "2026-01-15T10:30:00.000Z". כשעושים לה JSON.parse בחזרה, מקבלים את המחרוזת הזאת, לא אובייקט Date. השתמשו בארגומנט ה-reviver של JSON.parse כדי להמיר מחרוזות ISO בחזרה למופעים של Date אם אתם צריכים אותם.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל