C#: JSON والتسلسل

1. أساسيات تنسيق JSON

JSON (ترميز كائنات جافا سكريبت) هو تنسيق خفيف الوزن لتبادل البيانات، يعمل على تخزين ونقل البيانات المنظمة في شكل نص عادي.

TEXT 📖 للعرض فقط
{
  "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.

CSHARP
using System.Text.Json;

3. التسلسل: تحويل الكائن إلى JSON

JsonSerializer.Serialize() يحول كائنًا إلى سلسلة JSON.

▶ مثال

CSHARP
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);
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
{"Name":"Zhang San","Age":28,"IsStudent":false}

4. إزالة التسلسل: تحويل JSON إلى كائن

تقوم JsonSerializer.Deserialize``<T>``() بتحويل سلسلة JSON مرة أخرى إلى كائن من النوع المحدد.

▶ مثال

CSHARP
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}");
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Name: Zhang San, Age: 28

5. تكوين JsonSerializerOptions

استخدم JsonSerializerOptions للتحكم في سلوك التسلسل، مثل سياسة التسمية، والتباعد، وتجاهل القيم الفارغة.

الخيار الوصف
PropertyNamingPolicy سياسة تسمية الخصائص، على سبيل المثال JsonNamingPolicy.CamelCase
WriteIndented هل يجب وضع مسافة بادئة في النص الناتج لتسهيل القراءة
IgnoreNullValues هل يتم تجاهل الخصائص ذات القيمة الفارغة
PropertyNameCaseInsensitive تجاهل حالة الأحرف في أسماء الخصائص أثناء عملية إزالة التسلسل

▶ مثال

CSHARP
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);
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
{
  "name": "Zhang San",
  "age": 28
}

6. تحويل المجموعات والقواميس إلى تسلسل

يمكن لـ JsonSerializer تحويل أنواع المجموعات، مثل القوائم والقواميس، إلى صيغة تسلسلية مباشرةً.

▶ مثال

CSHARP
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);
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
[
  {
    "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 لإجراء تحليل ديناميكي دون الحاجة إلى فئات محددة مسبقًا.

▶ مثال

CSHARP
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()}");
        }
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Name: Zhang San, Age: 28
Hobby: programming
Hobby: reading

8. التسلسل المخصص: JsonConverter

قم بالوراثة من JsonConverter<T> وقم بتجاوز الطريقتين Read وWrite لتنفيذ منطق التسلسل/إلغاء التسلسل المخصص.

▶ مثال

CSHARP
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}");
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
{"Title":"Product Launch","Date":"2025-06-15"}
Title: Product Launch, Date: 2025-06-15

9. دمج JSON مع LINQ

بعد تحويل JSON إلى مجموعة، يمكنك استخدام LINQ لإجراء عمليات الاستعلام والتصفية والتجميع.

▶ مثال

CSHARP 📖 للعرض فقط
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}");
        }
    }
}
41 سطر من الكود المنطقي (تجاوز الحد 40, للعرض فقط)
TEXT 📖 للعرض فقط
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.

▶ مثال

CSHARP
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]}");
        }
    }
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Name       Age   City
Zhang San  28    Beijing
Li Si      25    Shanghai

💡 نصيحة: بالنسبة لبيئات الإنتاج، يُنصح باستخدام CsvHelper (حزمة NuGet)، التي تتعامل بشكل صحيح مع الحقول التي تحتوي على فواصل وعلامات اقتباس وأسطر جديدة، مما يساعد على تجنب الأخطاء الشائعة التي تحدث عند التحليل اليدوي.


❓ أسئلة شائعة

س ما الفرق بين System.Text.Json و Newtonsoft.Json؟
ج System.Text.Json هي مكتبة عالية الأداء مدمجة في .NET، بينما Newtonsoft.Json هي مكتبة تابعة لجهة خارجية وتتميز بميزات أكثر ثراءً. وينبغي تفضيل المكتبة المدمجة في المشاريع الجديدة.
س ماذا لو اختلفت أحرف أسماء الخصائص بين الأحرف الكبيرة والصغيرة أثناء عملية إزالة التسلسل؟
ج قم بتعيين PropertyNameCaseInsensitive = true في JsonSerializerOptions لتجاهل الاختلافات في الأحرف الكبيرة والصغيرة.
س كيف يمكنني استبعاد خاصية ما من عملية التسلسل؟
ج أضف السمة [JsonIgnore] إلى تلك الخاصية لتخطيها.
س كيف يمكنني التحكم في تنسيق التاريخ في JSON؟
ج قم بإنشاء JsonConverter<DateTime> مخصص وقم بتجاوز طريقة Write لتحديد سلسلة تنسيق التاريخ المطلوبة.

📖 ملخص

📝 تمارين

  1. قم بتعريف فئة Book (Title، Author، Price، IsPublished)، وقم بتحويلها إلى سلسلة JSON ذات مسافات بادئة، ثم اعرضها، ثم قم بإعادة تحويلها إلى كائن
  2. قم بإنشاء List<Book> يحتوي على 3 كتب على الأقل، وقم بتحويله إلى صيغة JSON، ثم استخدم LINQ لتصفية الكتب التي يزيد سعرها عن 50
  3. اكتب JsonConverter مخصصًا يقوم بتحويل DateTime إلى صيغة "yyyy/MM/dd"، وتأكد من أن عملية إزالة التسلسل تعيدها بشكل صحيح
  4. تحويل Dictionary<string, string> إلى صيغة JSON وحفظه في ملف، ثم قراءته من الملف وإعادة تحويله إلى صيغة أصلية
  5. اكتب منطقًا يدويًّا لقراءة/كتابة ملفات CSV يتعامل مع الحقول التي تحتوي على فواصل (ضعها بين علامتي اقتباس مزدوجتين)
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%