JSON ל־Go

יצירת מבני Go עם תגיות json מ־JSON: טיפוס כל מספר לפי אופן כתיבתו, ומצביעים היכן שערך עלול לחסור — הכול בדפדפן שלך.

קלט
Go
type Root struct {
	Orders  []Order `json:"orders"`
	HasMore bool    `json:"has_more"`
}

type Order struct {
	ID        int64    `json:"id"`
	UserID    int64    `json:"user_id"`
	CreatedAt string   `json:"created_at"`
	Price     float64  `json:"price"`
	Coupon    *string  `json:"coupon"`
	GiftNote  *string  `json:"gift_note,omitzero"`
	Shipping  Shipping `json:"shipping"`
	Tags      []any    `json:"tags"`
}

type Shipping struct {
	City     string          `json:"city"`
	Postcode json.RawMessage `json:"postcode"`
}
  • בכל אחד מהמיקומים שלהלן, כל מספר נקרא כמספר שלם, ולכן הטיפוס שם הוא int64. ערך שנכתב עם נקודה עשרונית או עם מעריך, אפילו 10.0, לא יפוענח לתוכו.

    מיקומים: ‎2

    • Order.ID
    • Order.UserID
  • בכל אחד מהמיקומים שלהלן, הערכים הם מיותר מסוג אחד — למשל מספרים ומחרוזות — ולכן הטיפוס שם הוא json.RawMessage, ששומר כל ערך בדיוק כפי שהוא כתוב ב־JSON שלכם, כדי שהתוכנית שלכם תפענח אותו כשתדע מאיזה סוג הוא. כשאחד הסוגים הוא אובייקט, המבנה שלו עדיין מופיע בפלט, וניתן לפענח את הערך הזה לתוכו.

    מיקומים: ‎1

    • Shipping.Postcode
  • בכל אחד מהמיקומים שלהלן לא היה ממה להסיק טיפוס: שם נראה רק null, המערך היה תמיד ריק, לאובייקט לא היו מפתחות, או שהערך מקונן עמוק מכדי שהכלי הזה יעקוב אחריו. ולכן הטיפוס שם הוא any, שמקבל כל ערך — או map[string]any היכן שלאובייקט לא היו מפתחות, שמקבל כל אובייקט או null ושום דבר אחר.

    מיקומים: ‎1

    • Order.Tags[]

מבנים שמפענחים את ה־JSON שממנו נוצרו

הדביקו דוגמת JSON, והעמוד הזה יכתוב עבורה הצהרות Go: מבנה בעל שם לכל אובייקט שבדוגמה, ותגית json על כל שדה, שנוקבת במפתח שהשדה קורא. הבטחה אחת מכריעה את כל מה שלהלן. בתוכנית שנבנית עם Go 1.27, ההצהרות מפענחות את ה־JSON שהדבקתם דרך "encoding/json" בלי שגיאה, וכל מפתח נקרא לתוך שדה, חוץ ממפתח שאף תגית מבנה לא יכולה לשאת, והוא מושמט. היכן ש־Go לא מאפשר להבטחה הזאת להתקיים, או מכריח את העמוד לבחור משהו שהנתונים שלכם לא הכריעו, ממצא מתחת לפלט אומר היכן — פרט לחריג נדיר אחד, שמתואר בפרק על התגיות.

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

שלושה טיפוסי מספרים, שנבחרים לפי האופן שבו כל מספר כתוב

דפדפן שקורא JSON הופך את 10 ואת 10.0 לאותו מספר. ב־Go זה לא כך: שדה "int64" דוחה מספר שנכתב עם נקודה עשרונית או עם מעריך — 10.0 או 1e3, ואפילו ‎-0.0 — בעוד שהוא מקבל 11 ו־‎-0. לכן העמוד לא בוחר טיפוס מספרי מתוך הערכים בלבד. הוא שואל את המנתח של הדפדפן עצמו איך כל מספר נכתב, ומחליט לפי זה:

  • היכן שכל מספר במיקום מסוים כתוב כמספר שלם, בלי נקודה עשרונית ובלי מעריך, השדה הוא "int64", וממצא המספרים השלמים נוקב בו.
  • כל מספר אחר הוא "float64", ולכן מחיר שנכתב 10.0 הוא "float64" גם כשכל מחיר בדוגמה עגול. המודול "json" של Python כותב float שערכו שלם בדיוק כך, ולכן API שמאחוריו שירות Python הוא המקום הרגיל לפגוש את זה.
  • "json.Number" שמור למספרים שאף אחד משני הטיפוסים לא מחזיק בדיוק: מספר שלם מעבר לאחד מקצוות הטווח של "int64", מספר גדול מדי עבור "float64" בכלל, כמו 1e999, ומספר שלם ארוך מדי עבור "float64" לצד שבר, כמו ב־‎[9007199254740993, 1.5]. הוא מחזיק כל אחד מהם בדיוק, והתוכנית שלכם ממירה אותו במקום שבו היא משתמשת בערך.

מספר שלם ארוך בין מספרים שלמים אחרים לא צריך זהירות כזאת: ‎[9007199254740993, 1] הוא "‎[]int64". שבר עם יותר ספרות ממה ש־"float64" שומר נשאר "float64" ומקבל ממצא משלו, כי אחרי פענוח וקידוד מחדש, 0.30000000000000001 חוזר כ־0.3.

כדי לקרוא איך מספר נכתב צריך דפדפן שמדווח על כך. דפדפן שלא מדווח קורא כל מספר מתוך הערך שלו בלבד, ולכן 10.0 נראה שם שלם והשדה שלו הופך ל־"int64", שלא יפענח את ה־JSON הזה; העמוד סופר את המספרים שלא הצליח להבחין בינם לבין מספרים שלמים, ומראה היכן נמצא הראשון שבהם. דפדפן כזה גם לא רואה את הספרות ש־"float64" מאבד, ולכן שם מספר שלם ארוך לצד שבר מקבל את הטיפוס "float64", ושום ממצא לא נוקב בשבר שמתעגל.

מצביעים היכן שערך עלול לחסור

מפתח שחלק מהאובייקטים משמיטים, או מציבים בו null, הוא מצביע: "‎*string" או "‎*int64", או מצביע למבנה שנכתב עבור אובייקט. גם האיברים של מערך שמחזיק null הם מצביעים, ולכן ‎[1, null] הוא "‎[]‎*int64". מפתח שחסר בחלק מהאובייקטים מסומן בנוסף ב־"omitzero" בתגית שלו. כששני אלה במקומם, שדה יכול לומר אם הגיע ערך, וקידוד מחדש של המבנה המפוענח משמיט שוב מפתח שהדוגמה רק השמיטה, וכותב בחזרה כ־null מפתח שהיא רק הציבה בו null.

לא ניתן מצביע ל־slice, ל־map, ל־"any" ול־"json.RawMessage", כי כל אחד מהם כבר nil כששום דבר לא פוענח לתוכו. עם זאת, slice מקבל "omitzero" היכן שהמפתח שלו עלול לחסור, ומערך שהופיע אבל היה ריק נכתב בחזרה כ־[].

הבחנה אחת לא שורדת. מפתח שחסר בחלק מהאובייקטים ושווה null באחרים הוא nil יחיד ב־Go, ולכן התוכנית שלכם לא יכולה להבחין בין השניים, וקידוד מחדש של המבנה משמיט את המפתח גם היכן שבדוגמה היה null; העמוד נוקב בכל שדה שזה קורה לו. מפתח מסוגים מעורבים שומר על ההבדל, כי "json.RawMessage" מחזיק את null בתור הבייטים null.

json.RawMessage לסוגים מעורבים, ו־any היכן שלא נראה דבר

כשהערכים של מפתח חלוקים בסוגם — מספר במקום אחד, מחרוזת או אובייקט במקום אחר — השדה הוא "json.RawMessage": הבייטים של הערך עצמו, שנשארים לתוכנית שלכם לפענח אחרי שהיא בודקת איזה סוג הגיע. גם null ביניהם לא משנה דבר. היכן שאחד הסוגים הוא אובייקט, המבנה שחולץ עבורו עדיין מודפס לצד האחרים. גם "any" היה מקבל כל סוג, והוא לא משמש לתערובת, כי מספר שמפוענח לתוך "any" הופך ל־"float64" ומאבד כל מה ש־"float64" לא יכול להחזיק.

יש חלקים בדוגמה שאין בהם ערך ללמוד ממנו: מפתח שתמיד החזיק רק null, מערך שהיה ריק בכל פעם, אובייקט בלי מפתחות, וערך שמקונן עמוק יותר ממה שההסקה קוראת. Go נותן "any" למפתח ה־null ולערך המקונן, "‎[]any" לאיברים של המערך הריק, ו־"map[string]any" לאובייקט. "any" מקבל כל סוג של ערך. ה־map מקבל אובייקט או null, ואילו מחרוזת, מספר, מערך או ערך בוליאני במקומו גורמים לפענוח להיכשל.

מעבר לעומק הזה יש שני דברים שהממצאים לא אומרים. מפתח שם שחסר בחלק מהאובייקטים ושווה null באחרים נזכר רק כמיקום שלא היה ממה להסיק בו טיפוס, ו־Go משמיט אותו בכתיבה בחזרה היכן שהיה null. ומספר שלם ארוך יותר ממה ש־"float64" מחזיק חוזר מעוגל, 9007199254740993 חוזר כ־9007199254740992, כי המיקום הוא "any".

שמות שדות בסגנון של Go, ושמות מבנים שמשותפים לעמוד ה־TypeScript

השם של שדה הוא האיות של Go למפתח שלו. המפתח מפוצל למילים בכל תו שאינו אות או ספרה, ובכל מקום שבו אחרי אות קטנה באה אות גדולה; אחר כך המילים מחוברות, כל אחת פותחת באות גדולה, ולכן "user_name", "last-name" ו־"firstName" הופכים ל־UserName, ל־LastName ול־FirstName. מילה שמופיעה ברשימת ראשי התיבות שמוגדרת כברירת מחדל ב־staticcheck נכתבת באותיות גדולות — המפתח "id" נכתב ID, המפתח "api_key" נכתב APIKey והמפתח "video_url" נכתב VideoURL — אבל רק מילה שלמה נחשבת, ולכן "idle" נכתב Idle, וצורת הרבים "ids" נכתבת Ids. מפתח שכתוב כולו באותיות גדולות נקרא כמילים: "USER_ID" נכתב UserID.

האותיות של המפתח עצמו נשמרות, ולכן "имя" הופך ל־Имя. היכן ששם לא היה פותח באות גדולה — מפתח בכתב שאין בו אותיות גדולות, כמו "名前", מפתח שפותח בספרה, כמו "1st", או אות ראשונה שאין לה אות גדולה בת תו אחד, כמו "ß" — הוא מקבל X בהתחלה, כמו X名前, X1st ו־Xß, והשדה קורא את המפתח שלו בכל זאת. דיאקריטים משולבים מושמטים מהשם ונשמרים בתגית. מפתח שאין בו אות או ספרה בכלל, כמו "@", נקרא Field, ושם שכבר בשימוש באותו מבנה מקבל מספר: "user_id", "userId" ו־"USER_ID" יחד הם UserID, UserID2 ו־UserID3.

הסגנון של Go מגיע עד שמות השדות ונעצר שם: כל מבנה שומר את השם שההסקה בחרה עבור האובייקט שלו, כך שההצהרות כאן והממשקים בעמוד ה־TypeScript נקראים באותם שמות, ושדה יכול להיכתב אחרת מהמבנה שהוא מחזיק. איך השם הזה נבחר מוסבר במדריך שבעמוד ה־TypeScript.

תגיות ל־Go 1.27, והמפתחות שאף תגית לא יכולה לשאת

התגית של כל שדה נוקבת במפתח שלו בדיוק, כמו json:"user_id"‎, ועם ‎,omitzero אחרי המפתח היכן שהמפתח עלול לחסור. תו בקרה במפתח נכתב ברצף ה־escape ש־Go עצמו משתמש בו, והמפתח "-" נכתב json:"-,"‎, צורה ש־Go קורא בתור המפתח הזה.

התגיות נכתבות עבור Go 1.27. בגרסה Go 1.26 נקראים דרך תגית פחות מפתחות: מפתח שמכיל משהו מלבד אותיות, ספרות, רווח של ASCII וקבוצה של סימני פיסוק של ASCII — כמו "Price (€)", "temp °C", אמוג׳י, תו בקרה או דיאקריט משולב — לא נקרא שם, והעמוד נוקב בכל שדה כזה. החצי הזה נבדק מול Go 1.27 שנבנה עם "GOEXPERIMENT=nojsonv2", שקורא תגיות כמו Go 1.26 אבל עם טבלאות ה־Unicode החדשות יותר של Go 1.27, ולכן מפתח שמכיל אות שאין בטבלאות הישנות יותר של Go 1.26 נקרא בבדיקה הזאת ולא מעלה שום ממצא.

מפתח שמכיל פסיק, בקסלאש, מירכאות, גרש או גרש הפוך, או המפתח הריק, הוא מפתח שאף תגית מבנה לא יכולה לשאת, ולכן הוא לא מקבל שדה בכלל. ה־JSON שלכם עדיין מפוענח: "json.Unmarshal" מדלג על המפתח הזה בלי להתלונן, אם כי "json.Decoder" שהוגדר עם "DisallowUnknownFields" נעצר בו. הממצא מפרט מפתח כזה לפי המבנה שלו ולפי המפתח, מצוטט כפי ש־Go מצטט מחרוזת, כמו Order["note,internal"], ולאובייקט שתחת מפתח כזה עדיין מודפס מבנה. מקרה אחד עובר בלי ממצא: שני מפתחות שנבדלים רק ב־surrogate בודד, רצף escape שאינו מייצג שום תו, הם מפתח אחד מבחינת Go, ואף אחד מהשדות שלהם לא מתמלא.

הצהרות בלבד, בפריסה של gofmt

הפלט מכיל הצהרות טיפוסים ותו לא: בלי שורת package ובלי import, ולכן הוא נכנס לקובץ שכבר יש לכם, תחת שורת ה־package של הקובץ עצמו. היכן שהוא משתמש ב־"json.RawMessage" או ב־"json.Number", הקובץ גם מייבא את "encoding/json", וזו השורה היחידה שנשארת לכם או לעורך שלכם. הפריסה היא של gofmt עצמו — טאב לפני כל שדה, והשמות, הטיפוסים והתגיות של כל מבנה בעמודות מיושרות — ולכן gofmt משאיר אותה בדיוק כמו שהיא. הטיפוס של השורש מודפס ראשון, כל אובייקט הוא טיפוס בעל שם ולא מבנה שנכתב בתוך השדה שמחזיק אותו, ו־JSON שהוא מערך או ערך בודד ברמה העליונה הוא גם כן טיפוס בעל שם, כמו "type Root []RootItem", כך שתמיד יש טיפוס לפענח אותו לתוכו.

מה הממצאים מבקשים מכם לבדוק

כל סוג של החלטה ש־Go כפה מקבל ממצא אחד מתחת להצהרות, והממצא מרכז את כל המיקומים שהוא חל עליהם, כך שדוגמה עם הרבה שדות של מספרים שלמים מקבלת ממצא אחד ולא ממצא לכל שדה. מיקומים נכתבים כמו שההצהרות כותבות אותם — Order.UserID הוא שדה, Order.Tags[]‎ הם האיברים של מערך, והשורש נכתב בשמו בלבד — חוץ משני ממצאים שמפנים לתוך ה־JSON שלכם לפי שורה ועמודה במקום זאת: מפתח שנכתב פעמיים, ומספרים שהדפדפן הזה לא הצליח להבחין בינם לבין מספרים שלמים. אף אחד מהממצאים לא משנה את ההצהרות. כדאי לקרוא כל אחד מהם כשאלה על הנתונים שלכם:

  • מספרים שלמים שקיבלו את הטיפוס "int64". כל מספר שם נכתב כמספר שלם. אם ערך שיגיע בהמשך עשוי לשאת נקודה עשרונית או מעריך, כמו שמחיר או מדידה יכולים, הוא לא יפוענח, ולכן הפכו את השדה הזה ל־"float64"; מזהה או ספירה יכולים להישאר כמו שהם.
  • הטיפוס "json.Number". אף טיפוס מספרי אחר לא מחזיק את הערכים האלה בדיוק. המירו כל אחד מהם במקום שבו התוכנית שלכם משתמשת בו, או, אם אתם יודעים שהערכים האמיתיים נכנסים לטיפוס צר יותר, שנו את השדה בעצמכם.
  • שבר ש־"float64" מעגל. למספר שם יש יותר ספרות ממה ש־"float64" שומר, ולכן התוכנית שלכם רואה ערך קרוב ולא את הערך שנכתב. היכן שכל ספרה חשובה, הפכו את השדה ל־"json.Number".
  • סוגים מעורבים שנשמרו כ־"json.RawMessage". הסתכלו על כל ערך כשהוא מגיע ופענחו אותו לפי הסוג שהתברר שהוא; היכן שאחד הסוגים הוא אובייקט, המבנה שלו עדיין נמצא בפלט, כדי לפענח את הערך הזה לתוכו.
  • אין ממה להסיק טיפוס. השדה הוא "any", או map של "any", כי בדוגמה לא היה שם ערך ללמוד ממנו. החליפו אותו בטיפוס שאתם יודעים שהשדה הזה נושא, או המירו שוב מ־JSON שבו יש לו ערכים אמיתיים.
  • חסר בחלק מהאובייקטים, null באחרים. Go שומר nil אחד לשניהם, ולכן המפתח מושמט בכתיבה בחזרה. זה משנה רק אם מה שקורא את הפלט שלכם מתייחס ל־null אחרת ממפתח שאינו קיים.
  • מפתחות ש־Go 1.26 לא קורא. בנו עם Go 1.27, או שנו את שמות המפתחות האלה במקום שבו הם נוצרים; ב־Go 1.26 הם לא נקראים.
  • מפתחות שהושמטו. אין להם שדה. כדי לקרוא אחד מהם, פענחו את האובייקט אל "map[string]json.RawMessage" או כתבו למבנה מתודת "UnmarshalJSON", כפי שהממצא עצמו מציע.
  • מפתח שנכתב פעמיים. הטיפוסים נבנים לפי העותק האחרון של המפתח, אבל Go מפענח כל עותק בתורו, ולכן עותק מוקדם יותר שהטיפוס לא יכול להחזיק, כמו מחרוזת כשהעותק האחרון הוא מספר, גורם לפענוח להחזיר שגיאה, ומפתח שמופיע רק בעותק מוקדם יותר של אובייקט לא נקרא. תקנו את ה־JSON בשורה ובעמודה שמוצגות.
  • הדפדפן הזה אינו מבחין בין 10 ל־10.0. הוא לא מדווח איך מספר נכתב, ולכן שדה שקיבל את הטיפוס "int64" בגלל ערך שנכתב עם נקודה עשרונית או עם מעריך לא יפענח את ה־JSON שלכם. בדקו את השדות שהערכים שלהם נכתבו כך, או המירו את ה־JSON בדפדפן שמדווח על כך.

אין ממצא על תאריכים, על תבניות או על קבוצות קבועות של ערכים, כי העמוד אף פעם לא מנחש אחד כזה מתוך מחרוזת: "created_at" בדוגמה שהעמוד טוען נשאר "string", איך שלא ייראה.

שאלות נפוצות

למה המחיר שלי הוא float64 כשכל מחיר ב־JSON שלי הוא מספר עגול?
כי כל מחיר כתוב עם נקודה עשרונית, כמו 10.0, ושדה "int64" דוחה מספר שנכתב כך, עגול או לא. העמוד קורא איך כל מספר כתוב ולא רק את הערך שלו, ולכן השדה הוא "float64" וה־JSON שלכם מפוענח. אילו הטיפוס נקבע כ־"int64" לפי הערכים בלבד, הפענוח היה נעצר במחיר הראשון.
מתי מספר יוצא json.Number?
כשאחד הערכים שלו הוא מספר שלם מעבר לטווח של "int64", מספר גדול מדי עבור "float64", או מספר שלם ארוך שיושב לצד שבר, ש־"float64" היה מעגל. "json.Number" שומר כל אחד מהם מדויק, והממצא שמתחת לפלט מפרט כל שדה שהוא נבחר עבורו.
למה חלק מהשדות הם מצביעים, ומה omitzero עושה בתגיות?
שדה הוא מצביע היכן שהמפתח שלו חסר בחלק מהאובייקטים או שווה null בחלק מהם, כדי ש־nil יוכל לומר שלא הגיע ערך. "omitzero" נכתב היכן שהמפתח חסר בחלק מהאובייקטים: קידוד מחדש של המבנה משמיט אז את המפתח הזה, כמו שה־JSON שלכם עשה, במקום לכתוב עבורו null.
למה json.RawMessage למפתח מסוגים מעורבים, ולא any?
כי "json.RawMessage" שומר את הבייטים של כל ערך עד שהתוכנית שלכם מחליטה איך לקרוא אותם. גם "any" היה מקבל את הערכים, אבל מספר שמפוענח לתוך "any" הוא "float64", ומספר שלם ארוך מאבד שם את הספרות האחרונות שלו.
למה חלק משמות השדות מתחילים ב־X?
כי אחרת השם לא היה פותח באות גדולה: המפתח כתוב בכתב בלי אותיות גדולות, פותח בספרה, או פותח באות שאין לה צורת אות גדולה בת תו אחד. ה־X שומר בשם את האותיות של המפתח עצמו, והשדה עדיין קורא את המפתח שלו — X名前 קורא את "名前".
לאיזו גרסת Go המבנים זקוקים?
הם נכתבים ונבדקים עבור Go 1.27. גם האופן שבו Go 1.26 קורא את התגיות נבדק, דרך תחליף: Go 1.26 לא קורא מפתח שמכיל תו מחוץ לאותיות, לספרות, לרווח של ASCII ולקבוצה של סימני פיסוק של ASCII, כמו סימן מטבע או אמוג׳י, והעמוד נוקב בכל שדה שהמפתח שלו מושפע מזה — חוץ ממפתח שמכיל אות חדשה יותר מטבלאות ה־Unicode של Go 1.26, ומפתח כזה לא העמוד ולא הבדיקה הזאת יכולים לראות.
למה אחד המפתחות שלי חסר במבנה?
כי המפתח מכיל פסיק, בקסלאש, מירכאות, גרש או גרש הפוך, או שהוא ריק, ואף תגית מבנה לא יכולה לשאת מפתח כזה. ממצא מפרט אותו לפי המבנה שלו ולפי המפתח עצמו, כמו Root["a,b"]; שאר ה־JSON שלכם עדיין מפוענח, והממצא אומר איך לקרוא את המפתח הזה בדרך אחרת.
האם בטוח להדביק תגובה שמכילה טוקנים או סיסמאות?
כן. כל שלב רץ בתוך העמוד הזה על המחשב שלכם, והתגובה שאתם מדביקים לא הולכת לשום מקום אחר: אף שרת לא מקבל אותה ושום דבר לא שומר ממנה עותק. ההצהרות שהעמוד מפיק ממנה מכילות רק שמות שדות, תגיות וטיפוסי Go, כך שטוקן או סיסמה ב־JSON לא משאירים בהן שום עקבה מלבד שדה "string" שנקרא על שם המפתח שלו.

כלים קשורים

  • JSON ל־TypeScript

    היכן שהערכים של מפתח הם מיותר מסוג אחד, העמוד הזה שומר כל אחד מהם בדיוק כפי שהוא כתוב ב־JSON שלכם כדי שהתוכנית שלכם תפענח אותו, כי ב־Go אין איחוד. העמוד ההוא כותב את אותה צורה כטיפוסי TypeScript באותם שמות טיפוסים, עם איחוד במקום הזה, והמדריך שלו מסביר איך הצורה הזאת מחושבת.

  • JSON ל־Zod

    ב־Go אין טיפוס שהוא פשוט מספר של JSON, ולכן היכן שכל מספר כתוב כמספר שלם העמוד הזה נותן לשדה טיפוס של מספר שלם, וערך שנכתב עם נקודה עשרונית לא יפוענח לתוכו. העמוד ההוא כותב את אותה צורה כסכימות Zod באותם שמות טיפוסים, וסכימה בודקת את הנתונים בכל פעם שהם מגיעים ומקבלת גם שבר בשדה כזה.

  • בודק JSONPath

    בדיקת שאילתות JSONPath לפי RFC 9535 מול JSON.

  • מחולל טבלאות Markdown

    בניית טבלאות Markdown מתוך CSV, TSV או JSON ויישור עמודות.