JSON פורמט
עיצוב יפה של JSON בהזחה של שני רווחים, ארבעה רווחים או טאב, או דחיסה שלו לשורה אחת — ואם הקלט לא תקין, מוצגים השורה והעמודה המדויקות של השגיאה.
הפלט יופיע כאן
מה עושה מעצב ה־JSON הזה
JSON (JavaScript Object Notation) הוא הפורמט הנפוץ ביותר להעברת נתונים מובְנים בין תוכניות — תגובות API, קובצי הגדרות, שורות לוג ועוד. הוא תוכנן להיות קומפקטי, מה שגם הופך אותו לקשה לקריאה כשאובייקטים מקוננים כמה רמות או מגיעים בשורה אחת. הכלי לוקח כל JSON שתדביק ומעצב אותו בשתי דרכים: מעוצב יפה, עם הזחה עקבית כך שהמבנה ברור במבט; או דחוס, נקי מכל רווח אופציונלי כדי להיות קטן ככל האפשר לשליחה.
הוא גם מאמת תוך כדי עיצוב. מכיוון שהוא מפרסר את הטקסט לפני שהוא מעצב אותו מחדש, JSON לא תקין לעולם לא מפיק פלט מטעה — במקום זאת תקבל את השורה והעמודה שבהן הפרסור נכשל, בכל מקום שבו הוא מצליח להסיק אותן, כדי לקפוץ ישר לבעיה.
עיצוב יפה מול דחיסה — מתי כל אחד
שני המצבים משרתים מטרות הפוכות, ורוב זרימות העבודה משתמשות בשניהם בשלבים שונים:
- עצב יפה כשאתה קורא או מנפה — בוחן תגובת API, משווה שני payloads, או סוקר קובץ הגדרות ב־pull request. הזחה הופכת קיר טקסט לעץ ניתן-לעיון.
- דחוס כשאתה שולח — משבץ JSON ב־HTML, שומר אותו ב־cookie או במטמון, או שולח אותו בגוף בקשה שבו כל בית חשוב. JSON דחוס הוא אותם נתונים בדיוק, רק בלי הרווחים.
בורר ההזחה (2 רווחים, 4 רווחים או טאב) משפיע רק על פלט מעוצב. שני רווחים הם המוסכמה הנפוצה ביותר ב־JavaScript ובכלי אינטרנט; ארבעה רווחים או טאבים מתאימים לצוותים שמעדיפים אותם. מה שלא תבחר, התוצאה עדיין JSON תקין — ההזחה קוסמטית בלבד.
קריאת שגיאות ולידציה
כשה־JSON לא תקין, הכלי מדווח על השורה והעמודה של הבעיה הראשונה במקום הודעת "לא תקין" חשופה — למעט המקרה היחיד שהפרק על המקום שאליו מצביעים השורה והעמודה מתאר. הודעות שגיאה של מנועים שונות בין דפדפנים ולעיתים משמיטות מיקום, ולכן המיקום מחושב באופן עצמאי ומצביע על נקודה סבירה להתחיל בה. תקן את השגיאה הראשונה ובדוק שוב — תו אחד שגוי לרוב מפל לכמה בעיות לכאורה.
טעויות JSON נפוצות
JSON מחמיר יותר מאובייקט-ליטרל של JavaScript שהוא דומה לו. אלה השגיאות שהכי מפילות אנשים:
- פסיק נגרר: פסיק אחרי הפריט האחרון באובייקט או מערך תקין ב־JavaScript אבל לא ב־JSON.
- מרכאות בודדות: מחרוזות ומפתחות ב־JSON חייבים מרכאות כפולות. 'ערך' לא תקין; "ערך" תקין.
- מפתחות ללא מרכאות: כל מפתח אובייקט חייב להיות מחרוזת במרכאות, כך ש־{ name: "x" } חייב להפוך ל־{ "name": "x" }.
- הערות: ל־JSON אין תחביר הערות. // ו־/* */ יגרמו לשגיאת פרסור.
- מספרים מיוחדים: NaN, Infinity ו־-Infinity אינם מספרי JSON תקינים.
- תווי מרכאות שגויים: "מרכאות חכמות" שהודבקו מתוכנת מלל נראות כמו מרכאות אך הן תווים שונים ולא ייפרסרו.
לאן מצביעים השורה והעמודה שמדווחות
המיקום אינו המקום שבו השמטת משהו. הוא המקום שבו הפרסר נתקל בפעם הראשונה במשהו שאסור לו להיות שם, ואלה לרוב שני מקומות שונים. באובייקט שלמטה חסר לשורה 3 הפסיק שהיה צריך לסגור אותה — והכלי מדווח על שורה 4, עמודה 3: המרכאות הפותחות של המפתח הבא. עד שהמרכאות האלה הגיעו שום דבר לא היה שגוי, כי המסמך היה יכול להסתיים באופן תקין אחרי שורה 3; ולכן הפרסר לומד על ההשמטה רק כשהוא נתקל במשהו שאינו פסיק ואינו סוגר מסולסל.
{
"id": 42,
"name": "widget"
"price": 9.99
}- פסיק חסר מדווח בתו הראשון של מה שבא בהמשך, ובקובץ JSON עם הזחה כמו שלמעלה זו השורה הבאה: קרא את השורה שנקבה יחד עם זו שמעליה.
- פסיק מיותר מדווח בסוגר הסוגר: שורה 3, עמודה 1 עבור אובייקט שהזוג האחרון שלו יושב בשורה 2. הפסיק מבטיח זוג נוסף, והסוגר הוא מה שמפר את ההבטחה.
- מחרוזת שלא נסגרה מדווחת לרוב בסוף השורה שבה היא נפתחה ולא במרכאות הפותחות, כי המרכאות הבאות בשורה מאוחרת יותר עלולות לסגור אותה במקום אחר. מעבר שורה אינו יכול להופיע בתוך מחרוזת JSON, ולכן המעבר הוא התו הראשון שאסור לו להיות שם.
- שורה 1, עמודה 1 במסמך שנראה מושלם משמעה לרוב סימן סדר בתים. עורכים מסוימים כותבים אחד כזה בשמירה בקידוד UTF-8; הוא בלתי-נראה, הוא יושב לפני הסוגר הפותח, ול־JSON אין מקום בשבילו.
ובמקום שבו הכלי אינו מצליח להסיק שום מיקום, הוא מדווח על הכשל בלי מיקום במקום לנקוב בקואורדינטה מנוחשת. שורה ועמודה שנאמרות בביטחון ומצביעות על תחביר תקין לחלוטין ישלחו אותך לחפש במקום הלא נכון, וזה גרוע יותר מלדעת רק שהמסמך אינו נפרסר.
מה העיצוב משנה ומה הוא שומר
כמעט בכל מסמך התשובה היא רווחים ולא יותר מזה. אבל הכלי אינו עורך את הטקסט שלך: הוא מפרסר אותו לערכים אמיתיים וכותב את הערכים האלה מחדש, וחמישה דברים אינם שורדים את הנסיעה הזאת הלוך וחזור. אף אחד מהם אינו פגם בכלי — כל אחד הוא מה שמפרט JSON אומר על מספר או על אובייקט — וכל אחד שווה להכיר לפני שאתה מדביק את הפלט על המקור שלך.
- אותו מפתח שנכתב פעמיים: רק האחרון מהשניים שורד, כי אובייקט אינו יכול לשאת מפתח אחד פעמיים. RFC 8259 אומר שתוכנה שמקבלת אובייקט עם שמות כפולים מתנהגת באופן בלתי-צפוי, ופרסר אחר עשוי לשמור דווקא את הראשון — כך שגם על השאלה איזה מהשניים יישאר לך אין להסתמך.
- מספר שלם ארוך מחמש-עשרה ספרות: מספרי JSON נקראים כנקודה צפה בדיוק כפול, שמחזיקה במדויק כל מספר שלם עד שתיים בחזקת חמישים ושלוש — מספר בן שש-עשרה ספרות — ולכן שלם בן חמש-עשרה ספרות שורד תמיד, וארוך ממנו עלול לא לשרוד. הדבק 12345678901234567890 ויוצא 12345678901234567000. מזהים ארוכים ממסד נתונים הם הקורבן הרגיל: שמור אותם כמחרוזות אם אתה יכול.
- צורות עם חזקה ועם אפסים נגררים מנורמלות: 1e3 חוזר כ־1000, ו־1.50 כ־1.5. זה אותו מספר עצמו, כתוב בדרך התקנית.
- סדר גודל מחוץ למה שהייצוג הזה נושא חוזר כמשהו אחר: ל־1e400 אין ערך בדיוק כפול והוא חוזר כ־null, ו־1e-400 חוזר כ־0. שבר עשרוני ארוך מעוגל לדיוק שיש לייצוג, בדיוק כמו מספר שלם ארוך.
- רצף בריחה הופך לתו שהוא מסמן: \u00e9 חוזר כ־é, וזוג מציין שעבר בריחה חוזר כאמוג'י שהוא מאיית. לכל פרסר אלה אותה מחרוזת בדיוק; אחד משני הכתיבים פשוט קצר יותר.
סדר המפתחות נשמר כפי שכתבת אותו, עם חריג אחד ששווה להכיר: מפתח שכולו ספרות, שנקרא כמספר שלם לא-שלילי פשוט הקטן מארבעה מיליארד בקירוב, נחשב לאינדקס של מערך וחוזר לראש האובייקט שלו בסדר מספרי, לא משנה איפה שמת אותו. שום דבר אחר לא זז: אף מפתח אחר אינו מסודר מחדש, ואף אחד אינו מתווסף או מקבל שם חדש. אם משהו מזה חשוב לך, דחוס במקום לעצב יפה והשווה את התוצאה למקור שלך תו אחר תו. זו הדרך הקצרה ביותר לראות מה עשתה הנסיעה הלוך וחזור.
כשה־JSON שאתה מחפש נמצא בתוך מחרוזת
לוגים של webhooks, תורי הודעות ועמודות במסד נתונים נושאים לעיתים קרובות מאוד מסמך JSON שלם כערך מחרוזת בודד, כשכל מרכאות שבתוכו עברו בריחה. המסמך החיצוני תקין לחלוטין, ולכן הכלי מעצב אותו ומדווח שהוא תקין — והחלק שבאת לקרוא נשאר שורה ארוכה אחת של לוכסנים אחורניים. שום דבר לא נשבר: אלה שני מסמכים, אחד עטוף בתוך מחרוזת של השני.
{
"event": "order.created",
"payload": "{\"id\":42,\"total\":19.99}"
}לכן קריאתו דורשת שני מעברים. עצב כאן את המסמך החיצוני, העתק את מה שנמצא בין המרכאות של המחרוזת שאתה רוצה, בטל את הבריחה, והדבק את התוצאה בחזרה. הכלי בריחת מחרוזות JSON עושה את הצעד האמצעי הזה: כיוון ביטול הבריחה שלו מחזיר את הלוכסן האחורני והמרכאות שאחריו למרכאות כפולות לבדן, ואת השורה למסמך שהדף הזה יכול לעצב. ואם אתה שולט במה שהפיק את הקובץ, התיקון הטוב יותר נמצא במעלה הזרם: שלח את המטען כאובייקט מקונן ולא כמחרוזת, ואז אין צורך באף מעבר.
אובייקט אחד בכל שורה אינו מסמך אחד
קובצי לוג, ייצוא מ־API ונקודות קצה בזרימה מחזיקים בדרך כלל אובייקט JSON שלם בכל שורה; הפורמט נקרא JSON Lines, או NDJSON. כל שורה היא JSON תקין בפני עצמה, אבל הקובץ אינו מסמך JSON, כי מסמך JSON מחזיק בדיוק ערך אחד ברמה העליונה והקובץ הזה מחזיק כמה, אחד אחרי השני ובלי שום דבר שמחבר ביניהם.
{"level":"info","msg":"started"}
{"level":"warn","msg":"retrying"}
{"level":"error","msg":"gave up"}הדבק את זה כאן והכלי מדווח על שורה 2, עמודה 1: האובייקט הראשון הסתיים בצורה נקייה, ואז שני התחיל במקום שבו המסמך היה צריך להיגמר. משם יש שתי דרכים. עצב שורה אחת בכל פעם, וזה מה שאתה רוצה כשאתה קורא רשומת לוג אחת. או הפוך את הקובץ למסמך אחד — עטוף את השורות בסוגריים מרובעים ושים פסיק בסוף כל שורה חוץ מהאחרונה — וזה מה שאתה רוצה כשאתה עומד לטעון את הכול למשהו שמצפה למערך.
שאלות נפוצות
- האם ה־JSON שלי נשלח לשרת?
- לא. הפרסור, הוולידציה והעיצוב מתרחשים כולם בדפדפן שלך באמצעות JavaScript. שום דבר שתדביק אינו מועלה, נשמר או נרשם, כך שבטוח להשתמש בכלי עם payloads רגישים.
- האם העיצוב משנה את הנתונים שלי?
- כמעט בכל מסמך: לא. עיצוב יפה ודחיסה רק מוסיפים או מסירים רווחים בין אסימונים, והמפתחות, הערכים והמבנה חוזרים זהים. אבל יש חריגים, כולם תוצאה של פרסור הטקסט שלך לערכים אמיתיים לפני שהוא נכתב מחדש, והפרק על מה שהעיצוב משנה מונה את כולם.
- למה הוא מסדר מחדש או מעצב את המספרים שלי?
- הכלי מפרסר JSON לערכים אמיתיים ומסרסר אותם בחזרה, כך שמספרים מנורמלים לצורתם הקנונית (למשל 1e3 הופך ל־1000). לכל מספר שנקודה צפה בדיוק כפול מחזיקה במדויק, הערך אינו משתנה ורק הכתיב שלו נעשה תקני. עבור מספר שדורש יותר דיוק או יותר טווח משיש לייצוג הזה — שלם מעל חמש-עשרה ספרות, שבר עשרוני ארוך, או סדר גודל מחוצה לו לגמרי — הערך עצמו זז, והפרק על מה שהעיצוב משנה מסביר איך.
- האם הוא מתמודד עם קובצי JSON גדולים מאוד?
- הוא מתמודד עם payloads גדולים, אבל מכיוון שהכול רץ בדפדפן, קבצים גדולים במיוחד (עשרות מגה-בייט) עלולים להיות איטיים או להגיע למגבלות זיכרון תלוי במכשיר שלך.
- האם הוא שומר על סדר המפתחות באובייקט?
- כמעט תמיד כן: סדר המפתחות נשמר בדיוק כפי שהוא מופיע בקלט, והכלי אינו ממיין דבר מעצמו. החריג היחיד הוא מפתח שכולו ספרות ונקרא כמספר שלם לא-שלילי פשוט הקטן מארבעה מיליארד בקירוב — הוא נחשב לאינדקס של מערך וחוזר לראש האובייקט שלו בסדר מספרי. JSON מכנה אובייקט אוסף בלי סדר, ולכן שום דבר לא נשבר, אבל זה מפתיע; הפרק על מה שהעיצוב משנה נושא את הפירוט.
- מה ההבדל בין JSON לאובייקט JavaScript?
- JSON הוא פורמט טקסט להחלפת נתונים; אובייקט JavaScript הוא ערך בזיכרון. JSON מחמיר יותר: הוא דורש מפתחות ומחרוזות במרכאות כפולות, אוסר פסיקים נגררים והערות, ומתיר רק קבוצה קבועה של סוגי ערכים (מחרוזות, מספרים, בוליאנים, null, מערכים ואובייקטים).
- אפשר לעצב JSON5 או JSONC (JSON עם הערות)?
- לא. הכלי מאמת JSON תקני ומחמיר. JSON5 ו־JSONC מוסיפים הערות ונוחויות אחרות שאינן חלק ממפרט ה־JSON, ולכן ידווחו כשגיאות.
- האם מחרוזת או מספר הם JSON תקין בפני עצמם?
- כן. מסמך JSON הוא כל ערך יחיד, ולכן "שלום", 42, true ו־null הם כל אחד שלם ותקין, והכלי הזה מעצב את ארבעתם. לא תמיד היה כך: RFC 4627 (2006) דרש אובייקט או מערך ברמה העליונה, RFC 7159 הרפה מזה ב־2014, ו־RFC 8259 נושא היום את הכלל הרחב יותר. כל דבר שדוחה ערך בודד פועל לפי המפרט הישן.
- אפשר לעצב כאן JSON Lines או NDJSON?
- שורה אחת בכל פעם — כן: כל שורה היא מסמך JSON שלם. את כל הקובץ בבת אחת — לא: אלה כמה מסמכים ולא אחד, והכלי מדווח על שורה 2, עמודה 1, שם השני מתחיל. הפרק שלמעלה על אובייקט אחד בכל שורה מכסה את שני המוצאים.
- למה הוא מדווח על שורה 1, עמודה 1 במסמך שנראה נכון?
- כמעט תמיד בגלל תו בלתי-נראה לפני הסוגר הפותח, וכמעט תמיד בגלל סימן סדר בתים שעורך השאיר בשמירה בקידוד UTF-8. זהו תו אמיתי, אי אפשר לראות אותו, ול־JSON אין מקום בשבילו. שמור את הקובץ מחדש בקידוד UTF-8 בלי סימן סדר בתים, או מחק את התו הראשון ממש והדבק שוב.
- האם העיצוב זורק מפתח כפול?
- כן, וזה המקרה היחיד שבו הפלט מחזיק פחות מהקלט. מפתח שנכתב פעמיים באותו אובייקט משאיר רק את האחרון מהשניים, כי הכלי מפרסר את הטקסט שלך לערכים אמיתיים ואובייקט אינו יכול לשאת מפתח אחד פעמיים. גם השאלה איזה מהשניים שורד אינה ניתנת להעברה: RFC 8259 אומר שתוכנה שמקבלת אובייקט כזה מתנהגת באופן בלתי-צפוי, ופרסר אחר עשוי לשמור את הראשון.
כלים קשורים
- בריחת מחרוזות JSON
בריחת טקסט למחרוזת JSON, או קריאה של טקסט בָּרוּח בחזרה.
- השוואת JSON
השוו שני מסמכי JSON — סדר המפתחות והעיצוב אינם נחשבים.
- SQL פורמט
עיצוב ויפוי SQL — במגוון דיאלקטים.
- XML פורמט
עיצוב XML וּבדיקת תקינות מבנית — יפה או דחוס.