CodeGym /Kurslar /C# SELF /LINQ to XML: XDocument

LINQ to XML: XDocument, XElement

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

1. Giriş

LINQ-in ortaya çıxması .NET-də XML ilə işləmədə inqilab oldu. Əvvəllər XML-i dəyişmək üçün NodeList və XmlElement ilə qarışıq konstruksiyalar yazmalı idik, indi isə bütün əməliyyatları LINQ tərzində ifadə edə bilərik. Bu, uzun müddət çınqıl yollarında dolaşdıqdan sonra sürətli avtobanaya çıxmaq kimidir.

LINQ to XML üç əsas məsələnin həllini verir:

  • XML-in qurulması və oxunmasının sintaksisini sadələşdirir.
  • Sənədə naviqasiyanı kolleksiyalarla işləməyə oxşadır.
  • XML-i istənilən LINQ-sorğusu ilə inteqrasiya etməyə imkan verir.

Əsas siniflər: XDocument, XElement, XAttribute

LINQ to XML-ə dərinləşməzdən əvvəl üç əsas sütunla tanış olaq:

  • XDocument — sənədin kökü, bütün sənədin yerləşdiyi "kitabın kirişi".
  • XElement — hər hansı ayrıca XML düyünü (element). Depoda bir-qutu kimi düşünün: içində başqa qutular və ya mallar (elementlər, atributlar, mətn) ola bilər.
  • XAttribute — elementin atributu, məsələn, <User id="42" />.

Vizual sxem


+-----------------+
|   XDocument     |---------------------+
+-----------------+                     |
            |                      (Root XElement)
            v                           |
      +-------------+              +-----------------+
      |   XElement  |<-------------| XElement (root) |
      +-------------+              +-----------------+
            |                              |
        (XAttribute)                (nested XElements)

2. LINQ to XML ilə XML oxumaq

Gəlin təsəvvür edək ki, tədris tətbiqimizdə (məsələn, ünvan kitabçası) XML-fayldan kontaktları oxumaq istəyirik. Faylın strukturu belə olsun:

<Contacts>
  <Person id="1">
    <Name>İvan İvanov</Name>
    <Email>ivan@example.com</Email>
    <Phones>
      <Phone type="mobile">+12 999 123-45-67</Phone>
      <Phone type="home">+12 812 123-45-67</Phone>
    </Phones>
  </Person>
  <Person id="2">
    <Name>Maria Petrova</Name>
    <Email>maria@example.com</Email>
    <Phones>
      <Phone type="mobile">+12 921 123-45-67</Phone>
    </Phones>
  </Person>
</Contacts>

XML-i yaddaşa oxumaq

Əvvəlcə layihəyə direktivi əlavə edək:

using System.Xml.Linq;

İndi faylı və ya sətri oxuyuruq:

// Tutaq ki, xmlString XML-sənədini saxlayır, yoxsa XDocument.Load("contacts.xml") istifadə etmək olar
var doc = XDocument.Parse(xmlString);

// Person siyahısını alırıq
var persons = doc.Root.Elements("Person");

foreach (var person in persons)
{
    string name = person.Element("Name")?.Value ?? "İsimsiz";
    string email = person.Element("Email")?.Value ?? "E-poçt yoxdur";
    string id = person.Attribute("id")?.Value ?? "id yoxdur";
    
    Console.WriteLine($"Ad: {name}, Email: {email}, id: {id}");
}

- doc.Root kök elementi verir (Contacts).
- Elements("Person") bütün <Person> elementlərini qaytarır.
- Sonra lazım olan element/atributu .Element("...") və ya .Attribute("...") ilə alırıq.
- ?. operatoru element/atribut tapılmadıqda səhvdən qoruyur.

Analogiya

XElement ilə işləmək matryoshka açmaq kimidir: birini çıxarırsan, içində başqa elementlər var və s. Ancaq indi bunu bir yüngül sorğu ilə etmək olur, əl ilə dolaşmaq yerinə.

3. XML üzərində LINQ-sorğuları yazmaq

Bütün insanların bütün mobil telefonlarının siyahısını almaq istəyirsən? LINQ tərzində asanlıqla etmək olar!

var mobilePhones = doc
    .Descendants("Phone") // hər səviyyədə bütün Phone elementlərini axtarır
    .Where(p => (string)p.Attribute("type") == "mobile")
    .Select(p => p.Value);

foreach (string phone in mobilePhones)
{
    Console.WriteLine($"Mobil: {phone}");
}

- Descendants("Phone") sənəddəki bütün <Phone> teqlərini tapır.
- Attribute("type") atributu alır.
- (string) ilə cast etmək atributu sətirə və ya null-a çevirir.

Gözəl fəndlər

Mürəkkəb nested strukturları nəzərə almadan birdən çox səviyyəyə axtarış etmək olar. Sadə XmlDocument ilə bu cür rahat etmək çətindir.

4. XML-i "uçuş zamanı" yaratmaq və dəyişdirmək

XML-i yaddaşda birbaşa XElement və XDocument konstrukturları ilə yaratmaq rahatdır. Məsələn, sənədə yeni insan əlavə etmək (tətbiqimizdə ünvan kitabçasına yeni yazı əlavə etmək):

// Yeni Person yaradırıq
var newPerson = new XElement("Person",
    new XAttribute("id", "3"),
    new XElement("Name", "Sergey Novikov"),
    new XElement("Email", "sergey@example.com"),
    new XElement("Phones",
        new XElement("Phone", new XAttribute("type", "work"), "+12 812 987-65-43")
    )
);

// Kökə əlavə edirik
doc.Root.Add(newPerson);

// Yenidən fayla (və ya sətirə) yazırıq
doc.Save("contacts.xml");

Şərhlər:

- Elementləri "matryoshka" konstrukturları ilə iç-içə qoymaq olar: ilk arqument — tag, qalanlar isə içlikdir (elementlər, atributlar, mətn ola bilər).
- Atributlar həmişə birbaşa məzmundan əvvəl verilir (məsələn, <Phone type="work">...).

5. C# kolleksiyaları ilə qarşılıqlı əlaqə — LINQ sehrləri

Adi obyekt kolleksiyasını (məsələn, List<Person>) XML-ə serializasiya etmək və ya əksinə tez-tez lazım olur. Bu demək olar ki, bir sətrdə LINQ ilə edilir:

var people = new List<Person>
{
    new Person { Id = 1, Name = "İvan İvanov", Email = "ivan@example.com" },
    // və s.
};

var doc = new XDocument(
    new XElement("Contacts",
        people.Select(p =>
            new XElement("Person",
                new XAttribute("id", p.Id),
                new XElement("Name", p.Name),
                new XElement("Email", p.Email)
            )
        )
    )
);
Person sinfi belə görünür (tamlığı üçün):
public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
}

İndi C# kolleksiyasından XML-ə sadə, aydın və nəzarətli çevrilmə nümunəniz var!

6. Faydalı metodlar və fəndlər: XElementXDocument

Naviqasiya

  • .Elements("TagName") — bütün birbaşa child elementlər.
  • .Descendants("TagName") — cari elementin içində istənilən səviyyədə olan bütün elementlər.
  • .Parent — valideyn elementi.
  • .Ancestors() — elementin bütün nəsilləri (yolu tapmaq üçün faydalıdır).
  • .FirstNode, .LastNode, .NextNode, .PreviousNode — qonşular üzrə naviqasiya.

Şərtlə axtarış

var personsWithEmail = doc.Root
    .Elements("Person")
    .Where(p => !string.IsNullOrEmpty((string)p.Element("Email")));

Əlavə etmə və silmə

// Mövcud istifadəçiyə telefon əlavə edək
var maria = doc.Root.Elements("Person")
    .FirstOrDefault(p => (string)p.Element("Name") == "Maria Petrova");

maria?.Element("Phones")?.Add(
    new XElement("Phone", new XAttribute("type", "work"), "+12 812 111-22-33")
);

// id ilə istifadəçini silmək
doc.Root.Elements("Person")
    .Where(p => (string)p.Attribute("id") == "3")
    .Remove();

Yenidən sətir/fayla çevirmək

string xmlText = doc.ToString(); // indentlərlə
doc.Save("contacts.xml");

7. Faydalı nüanslar

LINQ to XML-i digər .NET yanaşmaları ilə müqayisə

Yanaşma Nə vaxt istifadə etmək Üstünlüklər Çatışmazlıqlar
XmlDocument
Köhnə, legacy kod XPath ilə inteqrasiya Çox kod, mürəkkəb API
XmlSerializer
Obyektlərin serializasiyası üçün C# ↔ XML asan serializasiya Elastiklik məhduddur
LINQ to XML
Bütün digər hallar: parse, redaktə Sadə, yığcam, güclü, LINQ ilə Bəzi hallarda daha çox yaddaş tələb edə bilər

Real layihələrdə və müsahibələrdə tətbiqi

  • Müəyyən C# siniflərinə dəqiq uyğunluq olmayan konfiqurasiya fayllarının parse edilməsi.
  • Məlumatın import/exportu (məsələn, istifadəçi datalarının çıxarılması/yüklənməsi).
  • Miqrasiyalar, "çirkli" və ya mürəkkəb XML strukturlarının emalı.
  • Tez çevrilmələr (məsələn, bir XML formatını başqa formata çevirmək).

"Niyə bunu bilməliyəm?" — müsahibədə sizdən mürəkkəb XML-i parse etmək və ya çevirmək istəyə bilərlər; LINQ to XML ilə bunu köhnə API-lərlə müqayisədə daha sadə və gözəl şəkildə edəcəksiniz.

8. LINQ to XML ilə işləyərkən tipik səhvlər

Səhv №1: yoxlama etmədən mövcud olmayan elementə müraciət.
Element yoxdursa, ?. operatorundan və ya null yoxlamasından istifadə edin. Əks halda NullReferenceException alıb icra zamanı gözlənilməz qəza yaşaya bilərsiniz.

Səhv №2: atributlar və elementlər arasında qarışıqlıq.
Atributlar və elementlər XML-də fərqli məfhumlardır. Bəzən məlumatlar onların arasında bölünmüş olur. Məsələn:

<User login="admin"><Status>active</Status></User>

login atributunun dəyərini almaq üçün .Attribute("login") istifadə edin, tag-in içindəkini almaq üçün isə .Element("Status").

Səhv №3: .Remove() metodunun davranışını düzgün anlamamaq.
.Remove() yaddaşdakı XML strukturunu dərhal dəyişir. O heç nə qaytarmır və silməni geri almaq olmaz. Yeni başlayanlar bəzən onun silinən elementləri qaytaracağını və ya dəyişiklik barədə siqnal verəcəyini düşünürlər — belə deyil.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION