C#: JSON والتسلسل
1. أساسيات تنسيق JSON
JSON (ترميز كائنات جافا سكريبت) هو تنسيق خفيف الوزن لتبادل البيانات، يعمل على تخزين ونقل البيانات المنظمة في شكل نص عادي.
- الكائن: مغلف بـ
{}، ويحتوي على أزواج من القيم والمفاتيح؛ ويجب أن تكون المفاتيح سلاسل نصية - مصفوفة: محاطة بعلامتي
[]، وهي قائمة مرتبة من القيم - أنواع البيانات: سلسلة، عدد، قيمة منطقية، قيمة فارغة، كائن، مصفوفة
{
"name": "Zhang San",
"age": 28,
"isStudent": false,
"hobbies": ["programming", "reading"],
"address": {
"city": "Beijing",
"zip": "100000"
}
}
2. نظرة عامة على System.Text.Json
System.Text.Json هي مكتبة JSON عالية الأداء مدمجة في .NET Core 3.0+، ولا تتطلب أي حزم خارجية. ويُوصى باستخدامها بدلاً من Newtonsoft.Json.
using System.Text.Json;
3. التسلسل: تحويل الكائن إلى JSON
JsonSerializer.Serialize() يحول كائنًا إلى سلسلة JSON.
▶ مثال
using System;
using System.Text.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
class Program
{
static void Main()
{
var person = new Person
{
Name = "Zhang San",
Age = 28,
IsStudent = false
};
string json = JsonSerializer.Serialize(person);
Console.WriteLine(json);
}
}
{"Name":"Zhang San","Age":28,"IsStudent":false}
4. إزالة التسلسل: تحويل JSON إلى كائن
تقوم JsonSerializer.Deserialize``<T>``() بتحويل سلسلة JSON مرة أخرى إلى كائن من النوع المحدد.
▶ مثال
using System;
using System.Text.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsStudent { get; set; }
}
class Program
{
static void Main()
{
string json = @"{""Name"":""Zhang San"",""Age"":28,""IsStudent"":false}";
Person person = JsonSerializer.Deserialize<Person>(json);
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
}
}
Name: Zhang San, Age: 28
5. تكوين JsonSerializerOptions
استخدم JsonSerializerOptions للتحكم في سلوك التسلسل، مثل سياسة التسمية، والتباعد، وتجاهل القيم الفارغة.
| الخيار | الوصف |
|---|---|
PropertyNamingPolicy |
سياسة تسمية الخصائص، على سبيل المثال JsonNamingPolicy.CamelCase |
WriteIndented |
هل يجب وضع مسافة بادئة في النص الناتج لتسهيل القراءة |
IgnoreNullValues |
هل يتم تجاهل الخصائص ذات القيمة الفارغة |
PropertyNameCaseInsensitive |
تجاهل حالة الأحرف في أسماء الخصائص أثناء عملية إزالة التسلسل |
▶ مثال
using System;
using System.Text.Json;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
class Program
{
static void Main()
{
var person = new Person
{
Name = "Zhang San",
Age = 28,
Email = null
};
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
IgnoreNullValues = true
};
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json);
}
}
{
"name": "Zhang San",
"age": 28
}
6. تحويل المجموعات والقواميس إلى تسلسل
يمكن لـ JsonSerializer تحويل أنواع المجموعات، مثل القوائم والقواميس، إلى صيغة تسلسلية مباشرةً.
▶ مثال
using System;
using System.Collections.Generic;
using System.Text.Json;
public class Student
{
public string Name { get; set; }
public int Score { get; set; }
}
class Program
{
static void Main()
{
var students = new List<Student>
{
new Student { Name = "Zhang San", Score = 90 },
new Student { Name = "Li Si", Score = 85 },
new Student { Name = "Wang Wu", Score = 92 }
};
var options = new JsonSerializerOptions { WriteIndented = true };
string json = JsonSerializer.Serialize(students, options);
Console.WriteLine(json);
var scores = new Dictionary`<string, int>`
{
["Chinese"] = 90,
["Math"] = 95,
["English"] = 88
};
string dictJson = JsonSerializer.Serialize(scores, options);
Console.WriteLine(dictJson);
}
}
[
{
"Name": "Zhang San",
"Score": 90
},
{
"Name": "Li Si",
"Score": 85
},
{
"Name": "Wang Wu",
"Score": 92
}
]
{
"Chinese": 90,
"Math": 95,
"English": 88
}
7. JSON الديناميكي: JsonDocument و JsonElement
عندما تكون بنية JSON غير مؤكدة، يمكنك استخدام JsonDocument وJsonElement لإجراء تحليل ديناميكي دون الحاجة إلى فئات محددة مسبقًا.
▶ مثال
using System;
using System.Text.Json;
class Program
{
static void Main()
{
string json = @"{""name"":""Zhang San"",""age"":28,""hobbies"":[""programming"",""reading""]}";
using JsonDocument doc = JsonDocument.Parse(json);
JsonElement root = doc.RootElement;
string name = root.GetProperty("name").GetString();
int age = root.GetProperty("age").GetInt32();
JsonElement hobbies = root.GetProperty("hobbies");
Console.WriteLine($"Name: {name}, Age: {age}");
foreach (JsonElement hobby in hobbies.EnumerateArray())
{
Console.WriteLine($"Hobby: {hobby.GetString()}");
}
}
}
Name: Zhang San, Age: 28
Hobby: programming
Hobby: reading
8. التسلسل المخصص: JsonConverter
قم بالوراثة من JsonConverter<T> وقم بتجاوز الطريقتين Read وWrite لتنفيذ منطق التسلسل/إلغاء التسلسل المخصص.
▶ مثال
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
public class DateOnlyConverter : JsonConverter`<DateTime>`
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.ParseExact(reader.GetString(), "yyyy-MM-dd", null);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("yyyy-MM-dd"));
}
}
public class Event
{
public string Title { get; set; }
[JsonConverter(typeof(DateOnlyConverter))]
public DateTime Date { get; set; }
}
class Program
{
static void Main()
{
var evt = new Event
{
Title = "Product Launch",
Date = new DateTime(2025, 6, 15)
};
string json = JsonSerializer.Serialize(evt);
Console.WriteLine(json);
Event deserialized = JsonSerializer.Deserialize<Event>(json);
Console.WriteLine($"Title: {deserialized.Title}, Date: {deserialized.Date:yyyy-MM-dd}");
}
}
{"Title":"Product Launch","Date":"2025-06-15"}
Title: Product Launch, Date: 2025-06-15
9. دمج JSON مع LINQ
بعد تحويل JSON إلى مجموعة، يمكنك استخدام LINQ لإجراء عمليات الاستعلام والتصفية والتجميع.
▶ مثال
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
public class Student
{
public string Name { get; set; }
public int Score { get; set; }
public string City { get; set; }
}
class Program
{
static void Main()
{
string json = @"[
{""Name"":""Zhang San"",""Score"":90,""City"":""Beijing""},
{""Name"":""Li Si"",""Score"":85,""City"":""Shanghai""},
{""Name"":""Wang Wu"",""Score"":92,""City"":""Beijing""},
{""Name"":""Zhao Liu"",""Score"":78,""City"":""Shanghai""},
{""Name"":""Qian Qi"",""Score"":95,""City"":""Beijing""}
]";
List<Student> students = JsonSerializer.Deserialize<List<Student>>(json);
var beijingTop = students
.Where(s => s.City == "Beijing")
.OrderByDescending(s => s.Score)
.ToList();
Console.WriteLine("Beijing students (sorted by score, descending):");
foreach (var s in beijingTop)
{
Console.WriteLine($" {s.Name}: {s.Score}");
}
var cityAvg = students
.GroupBy(s => s.City)
.Select(g => new { City = g.Key, Avg = g.Average(s => s.Score) });
Console.WriteLine("\nAverage score by city:");
foreach (var item in cityAvg)
{
Console.WriteLine($" {item.City}: {item.Avg:F1}");
}
}
}
Beijing students (sorted by score, descending):
Qian Qi: 95
Wang Wu: 92
Zhang San: 90
Average score by city:
Beijing: 92.3
Shanghai: 81.5
10. مقدمة حول قراءة/كتابة ملفات CSV
CSV (القيم المفصولة بفواصل) هو تنسيق نصي يفصل الحقول بفواصل، ويُستخدم عادةً لتصدير البيانات وتبادلها. لا تحتوي المكتبة القياسية للغة C# على دعم مدمج لتنسيق CSV؛ يمكنك تحليلها يدويًّا أو استخدام مكتبة خارجية مثل CsvHelper.
▶ مثال
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Program
{
static void Main()
{
var records = new List<string[]>
{
new[] { "Name", "Age", "City" },
new[] { "Zhang San", "28", "Beijing" },
new[] { "Li Si", "25", "Shanghai" }
};
string csvPath = "data.csv";
File.WriteAllLines(csvPath, records.Select(r => string.Join(",", r)));
string[] lines = File.ReadAllLines(csvPath);
foreach (string line in lines)
{
string[] fields = line.Split(',');
Console.WriteLine($"{fields[0],-10} {fields[1],-4} {fields[2]}");
}
}
}
Name Age City
Zhang San 28 Beijing
Li Si 25 Shanghai
💡 نصيحة: بالنسبة لبيئات الإنتاج، يُنصح باستخدام CsvHelper (حزمة NuGet)، التي تتعامل بشكل صحيح مع الحقول التي تحتوي على فواصل وعلامات اقتباس وأسطر جديدة، مما يساعد على تجنب الأخطاء الشائعة التي تحدث عند التحليل اليدوي.
❓ أسئلة شائعة
PropertyNameCaseInsensitive = true في JsonSerializerOptions لتجاهل الاختلافات في الأحرف الكبيرة والصغيرة.[JsonIgnore] إلى تلك الخاصية لتخطيها.JsonConverter<DateTime> مخصص وقم بتجاوز طريقة Write لتحديد سلسلة تنسيق التاريخ المطلوبة.📖 ملخص
- يستخدم JSON
{}للكائنات و[]للمصفوفات؛ وهو تنسيق عالمي لتبادل البيانات System.Text.Jsonهي مكتبة JSON المدمجة في .NET، وتستخدمSerialize/DeserializeللتحويلJsonSerializerOptionsيضبط سياسة التسمية، والمسافات البادئة، ومعالجة القيم الفارغة، وغير ذلكJsonDocument/JsonElementمناسبان لتحليل JSON ذي البنية غير المحددة- يتحكم
JsonConverter<T>المخصص في منطق التسلسل لأنواع محددة - يمكن دمج بيانات JSON التي تم تحويلها إلى مجموعات مع LINQ لإجراء عمليات الاستعلام والتجميع
- يمكن تحليل ملفات CSV يدويًّا؛ أما للاستخدام في بيئة الإنتاج، فيُوصى باستخدام مكتبة
CsvHelper
📝 تمارين
- قم بتعريف فئة
Book(Title، Author، Price، IsPublished)، وقم بتحويلها إلى سلسلة JSON ذات مسافات بادئة، ثم اعرضها، ثم قم بإعادة تحويلها إلى كائن - قم بإنشاء
List<Book>يحتوي على 3 كتب على الأقل، وقم بتحويله إلى صيغة JSON، ثم استخدم LINQ لتصفية الكتب التي يزيد سعرها عن 50 - اكتب
JsonConverterمخصصًا يقوم بتحويلDateTimeإلى صيغة"yyyy/MM/dd"، وتأكد من أن عملية إزالة التسلسل تعيدها بشكل صحيح - تحويل
Dictionary<string, string>إلى صيغة JSON وحفظه في ملف، ثم قراءته من الملف وإعادة تحويله إلى صيغة أصلية - اكتب منطقًا يدويًّا لقراءة/كتابة ملفات CSV يتعامل مع الحقول التي تحتوي على فواصل (ضعها بين علامتي اقتباس مزدوجتين)