poniedziałek, 15 marca 2010

LINQ to XML

Szukając informacji na temat LINQ można trafić na bardzo wiele przykładów odnoszących się do LINQ to SQL i LINQ to object, ale bardzo ciężko trafić na solidną literaturę odnośnie dostępu do plików XML. Zastanawia mnie tylko dlaczego jest taka sytuacja, przecież XML jest jednym z najpopularniejszych formatów przechowywania danych (po bazie danych oczywiście).

Nie wgłębiając się w filozoficzne zawiłości tego tematu spróbuje w prosty sposób opisać możliwości jakie daje LINQu podczas pracy z plikiem XML.

 1. Utworzenie pliku XML
Zacznijmy od podstaw czyli od tego w jaki sposób utworzyć prosty plik XML. W tym miejscu mam nadzieję, że czytelnik zna strukturę i budowę plików XML. Jeżeli nie polecam zapoznać się z materiałem zawartym np na stronie http://www.w3schools.com/xml/default.asp.
Przejdźmy więc do utworzenia prostego dokumentu XML:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            XDocument documentXML = new XDocument(
                new XDeclaration("1.0", "utf-8", "yes"),
                new XElement("Books",
                    new XElement("Book",
                        new XAttribute("Id", 0),
                        new XElement("Title", "Inżynieria oprogramowania"),
                        new XElement("Author", "Ian Sommerville"),
                        new XElement("Pages", 685),
                        new XElement("Publisher", "WNT"))));
            //Printing creating document
            Console.WriteLine(documentXML);
        }
    }
}

Jest to nowy sposób tworzenia dokumentów XML. Kiedyś stosowany DOM jest o wiele bardziej nieczytelny od zaprezentowanego powyżej. Trochę objaśnień do kodu. Za pomocą atrybutu XDocument definiujemy nowy dokument XML. Następnie tworzymy go i w konstruktorze tworzymy deklarację dokumentu XML. Kolejne zagłębienia definiują kolejno atrybuty oraz elementy dokumentu. Należy tutaj zwrócić uwagę na stawianie nawiasów, gdyż ma to kluczowe znaczenie podczas tworzenia drzewa dokumentu. Na końcu wyświetlamy zawartość naszego dokumentu.

2. Zapis pliku XML na dysk
Zapis na dysk jest bardzo prosty i wymaga wywołania jednej metody Save:

            //...
            documentXML.Save(@"d:\plik.xml");

3. Odczyt pliku XML z dysku
Odczyt, podobnie jak zapis jest niezwykle prosty:
  
            XDocument loadFromDisk = XDocument.Load(@"d:\plik.xml");
            Console.WriteLine(loadFromDisk);

4. Dodawanie nowych elementów
Aby dodać nowy element do naszego dokumentu:
1. Pobieramy obiekt do którego chcemy dodać dane
2. Używając metody Add() dodajemy nowe wartośći
Przykład:

            XDocument loadFromDisk = XDocument.Load(@"d:\plik.xml");

            XElement root = loadFromDisk.Root;
            root.Add(new XElement("Book",
                new XAttribute("Id", 1),
                new XElement("Title", "Podstawy programowania współbierznego i rozproszonego"),
                new XElement("Author", "Mordechai Ben-Ari"),
                new XElement("Pages", 332),
                new XElement("Publisher", "WNT")));

            Console.WriteLine(loadFromDisk);

Należy pamiętać, że zmiany są zapisywane w pamięci komputera a nie na dysku. Jeżeli chcemy aby dane zapisały się na dysku twardym należy wywołać metodę Save().

5. Przechodzenie po drzewie dokumentu
Przechodzenie po dokumencie jest niezwykle proste. Wystarczy zwykła pętla aby wyświetlić wszystkie interesujące nas atrybuty czy elementy. Kilka przykładów wraz z komentarzem dla utworzonego wcześniej dokumentu:

            XDocument loadFromDisk = XDocument.Load(@"d:\plik.xml");

            //1. Wyświetlenie wszystkich elementów w drzewie
            foreach (var item in loadFromDisk.Elements())
            {
                Console.WriteLine(item);
            }
            Console.WriteLine("-------------------------------------------------");
            //2. To samo co wyżej ale ze specyfikacją głównej gałęzi
            foreach (var item in loadFromDisk.Elements("Books"))
            {
                Console.WriteLine(item);
            }
            Console.WriteLine("-------------------------------------------------");
            //3. Wyświetlenie informacji o samych książkach
            foreach (var item in loadFromDisk.Elements("Books").Elements("Book"))
            {
                Console.WriteLine(item);
            }
            Console.WriteLine("-------------------------------------------------");
            //4. Wyświetlenie tytułów książek
            foreach (var item in loadFromDisk.Elements("Books").Elements("Book").Elements("Title"))
            {
                Console.WriteLine(item);
            }
            Console.WriteLine("-------------------------------------------------");
            //5. Wyświtlenie Id książek
            foreach (var item in loadFromDisk.Elements("Books").Elements("Book").Attributes("Id"))
            {
                Console.WriteLine(item);
            }

6. Wyszukiwanie elementów na podstawie parametrów
Wyszukiwanie w dokumencie nie jest skomplikowane. Jeżeli przykładowo szukamy książki której Id = 1, możemy to zrobić w ten sposób:

            XDocument loadFromDisk = XDocument.Load(@"d:\plik.xml");
            var query = from a in loadFromDisk.Root.Elements("Book")
                        where int.Parse(a.Attribute("Id").Value) == 1
                        select a;
            foreach (var item in query)
            {
                Console.WriteLine(item);
            }

Możemy także ograniczyć się do wyświetlenia tylko tytułów:

            XDocument loadFromDisk = XDocument.Load(@"d:\plik.xml");
            var query = from a in loadFromDisk.Root.Elements("Book")
                        where int.Parse(a.Attribute("Id").Value) == 1
                        select a;
            foreach (var item in query)
            {
                Console.WriteLine(item.Element("Title"));
            }

Można także skorzystać z dobrodziejstwa typów anonimowych i ułatwić sobie wyświetlanie wybranych elementów:

            XDocument loadFromDisk = XDocument.Load(@"d:\plik.xml");
            var query = from a in loadFromDisk.Root.Elements("Book")
                        where int.Parse(a.Attribute("Id").Value) == 1
                        select new
                        {
                            Id = int.Parse(a.Attribute("Id").Value),
                            Title = a.Element("Title").Value,
                            Author = a.Element("Author").Value,
                            Pages = int.Parse(a.Element("Pages").Value),
                            Publisher = a.Element("Publisher").Value
                        };
            foreach (var item in query)
            {
                Console.WriteLine(item.Id);
                Console.WriteLine(item.Title);
            }

Dzięki takiemu podejściu IntelliSense podpowie jakie parametry możemy wykorzystać. 

7. Uaktualnianie pliku XML
Uaktualnienie pliku można podzielić na 
a) uaktualnienie całego elementu 
b) uaktualnienie pod elementu

W pierwszym przypadku należy wyodrębnić element który chcemy uaktualnić. Możemy tu skorzystać z wielu dostępnych metod które oferuje klasa XElement:

            XDocument loadFromDisk = XDocument.Load(@"d:\plik.xml");
            var query = from a in loadFromDisk.Root.Elements("Book")
                         where int.Parse(a.Attribute("Id").Value) == 1
                         select a;

            foreach (var item in query)
            {
                item.ReplaceAll(new XAttribute("Id", 10),
                    new XElement("Title", "Robinson Crusoe"),
                    new XElement("Author", "Daniel Defoe"),
                    new XElement("Pages", 242),
                    new XElement("Publisher", "Wordsworth classics"));
            }

            Console.WriteLine(loadFromDisk);

Jak widać uaktualnienie całego elementu nie wymaga wielkiej pracy. Jeszcze łatwiej jest z modyfikowaniem pojedynczej wartości. Dla przykładu zmienimy autora książki o Id = 0:

            XDocument loadFromDisk = XDocument.Load(@"d:\plik.xml");
            var query = from a in loadFromDisk.Root.Elements("Book")
                         where int.Parse(a.Attribute("Id").Value) == 0
                         select a;

            foreach (var item in query)
            {
                item.Element("Author").Value = "John Doe";
                //item.SetElementValue("Author", "John Doe");
                //item.Element("Author").SetValue("John Doe");
            }

W komentarzach podałem alternatywne metody wykonania tego samego zadania. Należy pamiętać, że dane nie zostaną zapisane na dysk do pliku XML bez jawnego wywołania metody Save()!

8. Usuwanie elementów
Usuwanie elementów przebiega tak samo jak uaktualnianie czy też modyfikowanie. Wyszukujemy interesujący nas element a następnie wywołujemy metodę Remove():

            XDocument loadFromDisk = XDocument.Load(@"d:\plik.xml");
            var query = from a in loadFromDisk.Root.Elements("Book")
                        where int.Parse(a.Attribute("Id").Value) == 0
                        select a;

            foreach (var item in query)
            {
                item.Remove();
            }

Mam nadzieję, że dzięki tym przykładom ułatwię trochę pracę z plikami XML przy użyciu technologi LINQ.

Brak komentarzy:

Prześlij komentarz