מה זה?

שיעור הקודם שמרנו טקסט גולמי לקובץ — נוח לרשימת מחרוזות, אבל מה עם אובייקט Student שלם עם כמה שדות? אפשר “להמציא” פורמט טקסט משלנו ("דנה,25,true"), אבל אז צריך לכתוב ידנית גם קוד שיודע לפרק אותו חזרה, וכל שינוי במחלקה שובר את הפירוק. JSON פותר את זה: פורמט טקסט סטנדרטי שכמעט כל שפת תכנות (וכל API) יודעת לקרוא ולכתוב — System.Text.Json הופך אובייקט C# ל-JSON (Serialize) ובחזרה (Deserialize) אוטומטית.

מילות מפתח שחשוב לזכור

• JSON (JavaScript Object Notation) — פורמט טקסט סטנדרטי לייצוג נתונים מובנים, נתמך כמעט בכל שפה ובכל API

• Serialize — הפיכת אובייקט C# למחרוזת JSON (JsonSerializer.Serialize)

• Deserialize — הפיכת מחרוזת JSON בחזרה לאובייקט C# (JsonSerializer.Deserialize<T>)

• Property Naming — ברירת המחדל: שם ה-Property ב-JSON זהה לשם ב-C# (Name); אפשר להתאים בעזרת attributes אם צריך שם אחר

class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
    public List<int> Grades { get; set; }
}

Student student = new Student { Name = "Dana", Age = 20, Grades = new List<int> { 90, 85, 78 } };

// Serialize — object → JSON string
string json = JsonSerializer.Serialize(student);
Console.WriteLine(json);
// {"Name":"Dana","Age":20,"Grades":[90,85,78]}

File.WriteAllText("student.json", json);   // save to file — ties in with the IO lesson
// Deserialize — JSON string → C# object
string jsonFromFile = File.ReadAllText("student.json");
Student loaded = JsonSerializer.Deserialize<Student>(jsonFromFile);
Console.WriteLine(loaded.Name);   // Dana — a real C# object, not text
// an entire collection — List<Student> Serialize/Deserialize with the exact same syntax
List<Student> students = new List<Student> { student, new Student { Name = "Avi", Age = 22 } };
string jsonArray = JsonSerializer.Serialize(students, new JsonSerializerOptions { WriteIndented = true });
List<Student> loadedList = JsonSerializer.Deserialize<List<Student>>(jsonArray);

הסבר עיקרי

למה JSON ולא פורמט מומצא — פורמט אישי (כמו CSV פשוט עם פסיקים) שובר בקלות ברגע שערך מכיל פסיק בעצמו, או שצריך לייצג רשימה מקוננת בתוך אובייקט. JSON תומך במבנים מקוננים (Grades היא רשימה בתוך Student) בצורה סטנדרטית, וכל שפה/כלי יודעים לקרוא אותו בלי המצאה מחדש.

Serialize/Deserialize הם ראי אחד לשני — JsonSerializer.Serialize(student) ו-JsonSerializer.Deserialize<Student>(json) הם פעולות הפוכות: הראשונה הופכת מופע C# למחרוזת; השנייה בונה מופע C# חדש מהמחרוזת, עם אותם ערכים בדיוק. <Student> ב-Deserialize הוא חובה — C# צריכה לדעת לאיזה טיפוס לבנות את האובייקט, היא לא “מנחשת” מהתוכן.

Property Naming כברירת מחדל — System.Text.Json משתמש בשמות ה-Properties כמו שהם (Name, Age) — לא הופך אוטומטית ל-camelCase כמו שנפוץ ב-JavaScript. אם ה-API/הקובץ שעובדים איתו מצפה לשמות שונים (למשל student_name), משתמשים ב-attributes ([JsonPropertyName("student_name")]) — לא יידרש כאן, אך שימושי לדעת שקיים.

יתרונות

JSON הוא סטנדרט אוניברסלי — כמעט כל API/שפה יודעים לקרוא ולכתוב אותו; System.Text.Json מובנה ב-.NET, בלי צורך בחבילה חיצונית; תומך במבנים מקוננים (אובייקטים בתוך אובייקטים, רשימות) בצורה טבעית.

חסרונות

JSON לא תומך בכל טיפוס C# ישירות (למשל DateTime נשמר כמחרוזת בפורמט מסוים, לא כטיפוס מקורי); Deserialize על JSON לא-תואם למחלקת היעד (שדה חסר, טיפוס שגוי) עלול לזרוק חריגה או להשאיר ערכי ברירת מחדל בלי אזהרה ברורה.

נקודות חשובות

• Serialize = אובייקט → JSON; Deserialize = JSON → אובייקט, עם <T> מפורש

• JSON תומך במבנים מקוננים (אובייקטים, רשימות) בצורה סטנדרטית

• שמות Properties ב-JSON תואמים כברירת מחדל לשמות ב-C# (Name, לא name)

• JSON הוא הבסיס לתקשורת עם כמעט כל REST API — יופיע שוב ביחידת ASP.NET Core

טעויות נפוצות

• לשכוח <Student> ב-Deserialize<T> — הקומפיילר לא יודע לאיזה טיפוס לבנות בלי זה

• להניח שכל JSON חיצוני יתאים אוטומטית למחלקת C# בלי לבדוק שמות שדות והתאמת טיפוסים

• לנסות Serialize על מבנה מעגלי (אובייקט שמצביע על עצמו) — עלול לזרוק חריגה על עומק רקורסיה

סיכום

JSON הוא פורמט טקסט סטנדרטי לייצוג נתונים מובנים, נתמך כמעט בכל שפה ו-API. JsonSerializer.Serialize הופך אובייקט C# למחרוזת JSON; JsonSerializer.Deserialize<T> הופך מחרוזת JSON בחזרה לאובייקט C# מהטיפוס שצוין. שילוב עם שיעור ה-IO (File.WriteAllText/ReadAllText) מאפשר לשמור ולטעון אובייקטים שלמים לדיסק בפורמט סטנדרטי, במקום פורמט טקסט מומצא ושברירי.

דוקומנטציה רשמית

Microsoft Learn — System.Text.Json overview


תרגילים

תרגיל 1 — Serialize/Deserialize בסיסי

המשימה: צרו מחלקת Product עם Name ו-Price, המירו מופע ל-JSON עם Serialize, הדפיסו אותו, ואז המירו אותו בחזרה לאובייקט עם Deserialize<Product>.

בדיקה: האובייקט אחרי Deserialize מכיל בדיוק את אותם ערכים כמו המקורי.

תרגיל 2 — שמירה וטעינה מקובץ

המשימה: שמרו List<Product> לקובץ products.json עם Serialize+File.WriteAllText, ואז בהרצה נפרדת (או קוד נפרד) טענו אותה בחזרה עם File.ReadAllText+Deserialize.

בדיקה: הרשימה שנטענה מהקובץ זהה בתוכן לרשימה המקורית שנשמרה.


פרויקט מסכם

המשימה: בנו “מערכת הגדרות אפליקציה” ששומרת וטוענת הגדרות מ-JSON.

דרישות:

  1. class AppSettings עם לפחות 4 שדות (למשל Theme, Language, NotificationsEnabled, MaxItemsPerPage)
  2. פונקציה SaveSettings(AppSettings settings) ששומרת ל-settings.json עם WriteIndented = true
  3. פונקציה LoadSettings() שטוענת מהקובץ אם קיים, ומחזירה הגדרות ברירת מחדל אם הקובץ לא קיים (File.Exists)

בדיקה: שמירת הגדרות, שינוי ידני של הקובץ (למשל עריכה ב-Notepad), וטעינה חוזרת מחזירה את הערכים המעודכנים; מחיקת הקובץ וטעינה מחזירה הגדרות ברירת מחדל בלי לקרוס.

מה בפרק הבא

בפרק הבא — LINQ, חלק א’: איך מסננים וממיינים אוספים בשורה אחת עם Where/Select/OrderBy, במקום לולאות foreach ידניות עם דגלים ומשתני עזר.