CodeGym /Kurslar /C# SELF /Daxili obyektlər və kolleksiyalarla işləmək

Daxili obyektlər və kolleksiyalarla işləmək

C# SELF
Səviyyə, Dərs
Mövcuddur

1. Giriş

Sadə obyektlərin serialization-ı məktub göndərmək kimidir: hər şey sadədir, heç nə itmir. Amma tez-tez obyektlərimiz "ailə albomları" olur: onların içində daxili obyektlər, kolleksiyalar və hətta kolleksiyaların kolleksiyaları olur.

Təsəvvür edin ki, bizim istifadəçi sadəcə Person adı və yaşı olan obyekt deyil, məsələn, onun bir sıra kontaktları (Contact), bir neçə ev/iş ünvanı və bəlkə də List<Pet> tipində sahəsi var, əgər insan heyvanları sevirsə. Bütün bunlar — iç-içə obyektlər və kolleksiyalardır.

.NET serializer-ləri bununla necə davranır? Kompleks obyekt ağaclarını serialize edərkən hansı nüansları yadda saxlamaq lazımdır? Kolleksiyaların içində başqa kolleksiyalar olsa hər şey sıradan çıxar? Bugün biz nələri gözləmək lazım olduğunu və çətin hallarda nə etmək lazım olduğunu öyrənəcəyik.

“Daxili olma” .NET terminologiyasında nə deməkdir

Daxili obyekt — əsas obyektinizin içində property və ya field kimi olan başqa bir obyekt deməkdir. Məsələn, istifadəçinin genişləndirilmiş modelinə baxaq:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public List<Contact> Contacts { get; set; }         // Daxili obyektlərin kolleksiyası
    public Address? HomeAddress { get; set; }           // Bir daxili obyekt (nullable)
}
public class Contact
{
    public string Type { get; set; }                    // Məsələn, Email və ya Phone
    public string Value { get; set; }
}
public class Address
{
    public string City { get; set; }
    public string Street { get; set; }
}

Diqqət yetirin — insanın bir neçə kontakta malik ola bilər, amma ünvan yalnız biri ola bilər (və o null ola bilər). Bu bir çox modellərin klassik nümunəsidir.

2. System.Text.Json ilə obyektlərin və kolleksiyaların serialization-ı

Sadə nümunə: kolleksiyanı serialize və deserialize edək

Gəlin real həyata baxaq:

using System.Text.Json;

var person = new Person
{
    Name = "Anna",
    Age = 28,
    Contacts = new List<Contact>
    {
        new Contact { Type = "Email", Value = "anna@example.com" },
        new Contact { Type = "Phone", Value = "+1234567890" }
    },
    HomeAddress = new Address { City = "Berlin", Street = "Alexanderplatz, 1" }
};

string json = JsonSerializer.Serialize(person, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

Nəticə təxminən belə olacaq:

{
  "Name": "Anna",
  "Age": 28,
  "Contacts": [
    {
      "Type": "Email",
      "Value": "anna@example.com"
    },
    {
      "Type": "Phone",
      "Value": "+1234567890"
    }
  ],
  "HomeAddress": {
    "City": "Berlin",
    "Street": "Alexanderplatz, 1"
  }
}

Serializer daxili quruluşu yaxşı başa düşür. Əgər property başqa obyektdirsə, onu "daxili" olaraq serialize edir. Əgər siyahıdırsa — array kimi serialize olunur.

Fakt: Belə serialization istənilən kolleksiyalar və daxili obyektlər üçün avtomatik işləyir, əgər onlar public və public getter/setter-ə malikdirsə (get/set).

Deserialization: "qutudan çıxarılmış" halda işləyir

string jsonInput = /* Bizim yeni aldığımız JSON */;
Person deserializedPerson = JsonSerializer.Deserialize<Person>(jsonInput);
Console.WriteLine(deserializedPerson.Name); // "Anna"
Console.WriteLine(deserializedPerson.Contacts[0].Type); // "Email"

Hər şey işləyir — kontaktlar JSON array-dən geri List<Contact>-ə çevrilir, HomeAddress — geri Address obyektinə.

3. Kolleksiyalar necə serialize olunur: List, massivlər, Dictionary

Modelinizin içində tez-tez yalnız siyahılar (List<T>) yox, həmçinin sözlüklər (Dictionary<TKey, TValue>) və çoxölçülü massivlər də olur.

Massiv nümunəsi

public class Team
{
    public string Name { get; set; }
    public Person[] Members { get; set; }
}

var team = new Team
{
    Name = "Developers",
    Members = new[]
    {
        new Person { Name = "Aleksey", Age = 31 },
        new Person { Name = "Ekaterina", Age = 27 }
    }
};
string json = JsonSerializer.Serialize(team, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

JSON:

{
  "Name": "Developers",
  "Members": [
    {
      "Name": "Aleksey",
      "Age": 31,
      "Contacts": null,
      "HomeAddress": null
    },
    {
      "Name": "Ekaterina",
      "Age": 27,
      "Contacts": null,
      "HomeAddress": null
    }
  ]
}

Kolleksiyanın serialization-ı eyni tipdə bir çox məktubun qablaşdırılması kimidir: hər element ayrı "kart" olur.

Dictionary ilə nümunə

public class Phonebook
{
    public Dictionary<string, string> Phones { get; set; }
}

var phonebook = new Phonebook
{
    Phones = new Dictionary<string, string>
    {
        { "Andrey", "+12998887766" },
        { "Maria", "+12882223344" }
    }
};

string json = JsonSerializer.Serialize(phonebook, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

JSON:

{
  "Phones": {
    "Andrey": "+12998887766",
    "Maria": "+12882223344"
  }
}

JSON-də sözlük dinamik açarlı obyektə çevrilir.

4. Daxili kolleksiyalar (List içində List, massivlərin massivləri)

.NET belə "matryoshkaları" serializasiya etməyi də bilir:

public class Zoo
{
    public List<List<Animal>> AnimalGroups { get; set; }
}
public class Animal { public string Name { get; set; } }

var zoo = new Zoo
{
    AnimalGroups = new List<List<Animal>>
    {
        new List<Animal> { new Animal { Name = "Lev" }, new Animal { Name = "Tigr" } },
        new List<Animal> { new Animal { Name = "Medved" } }
    }
};

string json = JsonSerializer.Serialize(zoo, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

JSON:

{
  "AnimalGroups": [
    [ { "Name": "Lev" }, { "Name": "Tigr" } ],
    [ { "Name": "Medved" } ]
  ]
}

Deserialization mərhələsində də eyni qayda ilə geri alınır.

5. XmlSerializer-də daxili obyektlərin serialization xüsusiyyətləri

Daxili obyektin serialization nümunəsi

using System.Xml.Serialization;
using System.IO;

var person = new Person
{
    Name = "Ivan",
    Age = 35,
    HomeAddress = new Address { City = "Bonn", Street = "Beethoven str., 100" },
    Contacts = new List<Contact>
    {
        new Contact { Type = "Email", Value = "ivan@domain.de" }
    }
};

var serializer = new XmlSerializer(typeof(Person));
using var writer = new StringWriter();
serializer.Serialize(writer, person);
Console.WriteLine(writer.ToString());

Nəticə:

<?xml version="1.0" encoding="utf-16"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Ivan</Name>
  <Age>35</Age>
  <Contacts>
    <Contact>
      <Type>Email</Type>
      <Value>ivan@domain.de</Value>
    </Contact>
  </Contacts>
  <HomeAddress>
    <City>Bonn</City>
    <Street>Beethoven str., 100</Street>
  </HomeAddress>
</Person>

XmlSerializer-də kolleksiyaların xüsusiyyətləri

Default olaraq XmlSerializer kolleksiyanı "Contacts" teqi kimi serialize edir və hər element üçün ayrıca "Contact" teqi yaradır. Bunun üçün kolleksiya tipi public olmalı və onun elementləri serializable tiplər olmalıdır.

Mühüm məqam. Əgər kolleksiya boşdursa, XML hələ də boş teqi daxil edəcək:

<Contacts />

Dəstək olunan kolleksiyalar. XmlSerializer List<T>, massivlər T[], ICollection<T>-ni implement edən kolleksiyaları dəstəkləyir. Məsələn, Dictionary<TKey, TValue>-ni birbaşa dəstəkləmir! Sözlüklərin serialization-ı üçün əlavə texnikalar və ya üçüncü tərəf kitabxanalarından istifadə etmək lazım gəlir.

6. Newtonsoft.Json-də obyektlər və kolleksiyaların dəstəyi

Newtonsoft.Json daxili struktur dəstəkləməsində bir az daha çevikdir. 99% halda hər şey System.Text.Json kimi işləyir, amma üstünlükləri var: private field-ları serializasiya etmək (əgər açıqca göstərsəniz), daha mürəkkəb açarlı sözlüklər, dynamic tiplər və hətta dövrü referensiyalar (xüsusi ayarlarla) serializasiya etmək mümkündür.

Nümunə

using Newtonsoft.Json;

var person = new Person
{
    Name = "Pavel",
    Age = 40,
    Contacts = new List<Contact>
    {
        new Contact { Type = "SMS", Value = "+10000000013" }
    }
};
string json = JsonConvert.SerializeObject(person, Formatting.Indented);
Console.WriteLine(json);

Fərq. Məsələn, obyektin private field-ları varsa, onları ContractResolver ilə serialize etmək olar. Amma əsas daxili kolleksiyalar və obyektlər üçün hər şey "qutudan çıxarılmış" halda avtomatik işləyir.

7. Tipik səhvlər, tələ-fəlakətlər və nüanslar

Səhv 1: Daxili obyektlər — null.
Əgər serialize etdiyiniz obyektin bəzi property-ləri doldurulmayıb (null), JSON və ya XML-də onlar ola da bilər, olmaya da bilər, və ya null kimi yazıla bilər. Məsələn, əgər HomeAddress Person-da null-dırsa, JSON-da:

"HomeAddress": null

XML-də default olaraq belə bir teq olmayacaq — bunu serializer ayarlarına əlavə etməklə daxil etmək olar ([XmlElement(IsNullable=true)]).

Səhv 2: Private property/field-ların serialization-ı.
Default olaraq əksər serializer-lər (həm JSON, həm XML) yalnız public property-lərlə işləyir. Əgər private və protected field-ları serialize etmək istəyirsinizsə, onları public elan etməli və ya serializer-i konfiqurasiya etməlisiniz (məsələn, ContractResolver vasitəsilə Newtonsoft.Json-də).

Səhv 3: Default konstruktoru olmayan siniflər.
XML-serialization-da (və çox vaxt JSON-da) sinifin parametri olmayan public konstruktoru olmalıdır. Əgər belə konstruktor yoxdursa — deserialization zamanı exception alınacaq.

Səhv 4: Sözlüklərin XML-də serialization-ı.
XmlSerializer birbaşa Dictionary<TKey, TValue>-ni dəstəkləmir. Bu tez-tez gözlənilməz olur. Həll yolu kimi sözlüyü cütlüklər siyahısına bükmək və ya digər serializer-lərdən istifadə etmək olar.

Səhv 5: Dövrü referensiyalar.
Əgər bir obyekt başqa obyektə referensiya edir və həmin obyekt yenidən birinciyə referensiya edirsə (məsələn, parent-child əlaqəsi), serialization "dairəvi" ola bilər və bu StackOverflowException və ya dövrü referensiya ilə bağlı başqa exception-la nəticələnə bilər. JSON serializer-lərdə bunu ayarlarla həll etmək mümkündür, amma çox vaxt daha düzgün olan arxitekturanı nəzərdən keçirməkdir.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Mürəkkəb strukturu seriyalaşdırma və deserializasiya
Mürəkkəb strukturu seriyalaşdırma və deserializasiya
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION