LINQ TO XML Celem ćwiczenia jest zapoznanie się z możliwościami przetwarzania dokumentów XML na platformie.net. W toku zadania zostaną przedstawione dwie technologie: LINQ TO XML i XPath. Autor ćwiczenia: Marcin Wolicki 1. Pobierz pliki XML zawierające dane pracowników i zespołów: a. http://www.cs.put.poznan.pl/kjankiewicz/txml/xml-schema/prac.xml b. http://www.cs.put.poznan.pl/kjankiewicz/txml/xml-schema/zesp.xml Zapisz je na dysku i zapamiętaj ścieżkę do tych plików. 2. Otwórz VisualStudio 2010 i utwórz nowy projekt aplikacji konsolowej. W tym celu: a. Wybierz File > New > Project b. W oknie New Project zaznacz Console Application z Visual C# > Windows c. Wprowadź dowolną nazwę projektu (Name) - przykładowo WAI.XML d. Przykładowo wypełnione okno tworzenia nowego projektu:
Proste zapytania 3. Napisz funkcję zwracającą wszystkie ROW z pliku prac.xml. W tym celu: a. W klasie Program utwórz stałą zawierającą ścieżkę do pliku prac.xml private const string PracFile = @"c:/tmp/prac.xml"; b. Stwórz funkcję static void SimpleQuery i uzupełnij ją następującym kodem: var doc = XDocument.Load(PracFile); var aw = XNamespace.Get("www.pp.pl/prac"); var result = from d in doc.descendants(aw+"row") select d; foreach (var xelement in result) Console.WriteLine(xElement); i. Funkcja XDocument.Load wczytuje plik XML do pamięci i tworzy instancję klasy XDocument ii. XNamespace.Get jest wykorzystana w celu pobrania tych elementów dokumentu XML, które są kwalifikowane przez namespace www.pp.pl/prac c. Dodaj brakujący using: using System.Xml.Linq; d. Dodaj wywołanie funkcji SimpleQuery do Main: static void Main(string[] args) SimpleQuery(); Console.ReadLine(); e. Uruchom program za pomocą Debug > Start Debugging (F5) f. Wynik działającego programu:
g. Przykładowy kod całości rozwiązania: 4. Utwórz funkcję wyświetlającą na konsoli nazwiska osób zatrudnionych na etacie PROFESOR. W tym celu: a. Skopiuj funkcję SimpleQuery i zmień jej nazwę na SimpleQuery_Nazwisko b. Zmień zawartość zapytania LINQ na następującą: var result = from d in doc.descendants(aw + "ROW") where d.element(aw + "ETAT").Value == "PROFESOR" select d.element(aw + "NAZWISKO").Value; i. W porównaniu do poprzedniego zapytania doszła klauzula where umożliwiająca ograniczenie zbioru wynikowego. c. Dostosuj funkcję Main, tak by zamiast SimpleQuery uruchamiała SimpleQuery_Nazwisko d. Uruchom aplikację (Debug > Start lub F5)
e. Przykładowy wynik działania programu: f. Przykładowy kod całości rozwiązania: 5. Utwórz funkcję o analogicznej funkcjonalności jak powyżej ale z wykorzystaniem XPath. W tym celu: a. Stwórz nową funkcję SimpleXPath_Nazwisko. b. Uzupełnij ją następującym kodem: var doc = new XmlDocument(); doc.load(pracfile); var xmlns = new XmlNamespaceManager(doc.NameTable); xmlns.addnamespace("p", "www.pp.pl/prac"); var result = doc.selectnodes("//../p:nazwisko/text()", xmlns); if(result!=null) foreach (var element in result) Console.WriteLine(((XmlText) element).value);
c. Dodaj brakujący using: using System.Xml; d. Dokończ zapytanie XPath, tak by zwracało nazwiska tylko tych osób, które są zatrudnione na etacie profesor (przykładowe rozwiązanie znajduje się na końcu zadania) e. Analogicznie jak poprzednio dostosuj funkcję Main i uruchom projekt. f. Przykładowy wynik działania programu: g. Przykładowy kod całości rozwiązania:
6. Utwórz funkcję zwracającą element PROFESOR wraz z ID oraz nazwiskiem. ID dodatkowo niech będzie kwalifikowane przez namespace www.pp.pl/prac. a. Skopiuj metodę SimpleQuery i zmień jej nazwę na SimpleQuery_Nazwisko2 b. Tym razem wykorzystamy inną składnię LINQ. Dotychczas korzystaliśmy ze składni przypominającej język SQL. Tym razem wykorzystamy składnię opartą na metodach. Zamień zapytanie na następujące: var result = doc.descendants(aw + "ROW").Where(d => d.element(aw + "ETAT").Value == "PROFESOR").Select(d => new XElement("PROFESOR", new XAttribute(aw + "ID", d.element(aw + "ID_PRAC").Value), new XElement("NAZWISKO", d.element(aw + "NAZWISKO").Value) )); i. Klauzula where została zastąpiona metodą Where(), a select metodą Select(). ii. Obie składnie LINQ są równoważne. c. Przyjrzyj się w jaki sposób tworzone są nowe elementy i atrybuty. d. Przykładowy wynik działania programu: e. Przykładowy kod:
Łączenie dokumentów (Join) 7. W kolejnym ćwiczeniu napisz funkcję zwracającą element PRACOWNIK (składający się z nazwy zespołu i nazwiska pracownika). a. W celu wykonania polecenia, będziemy musieli połączyć dwa pliki zesp.xml oraz prac.xml. b. Analogicznie jak w pkt. 3 utwórz stałą przechowującą ścieżkę do pliku zesp.xml: private const string ZespFile = @"c:/tmp/zesp.xml"; c. Utwórz funkcję SimpleQuery_Join, która będzie łączyła dwa pliki: private static void SimpleQuery_Join() var doc = XDocument.Load(PracFile); var nsprac = XNamespace.Get("www.pp.pl/prac"); var nszesp = XNamespace.Get("www.pp.pl/zesp"); var result = from prac in doc.descendants(nsprac + "ROW") join zesp in XDocument.Load(ZespFile).Descendants(nsZesp + "ROW") on prac.element(nsprac + "ID_ZESP").Value equals zesp.element(nszesp + "ID_ZESP").Value select new XElement("PRACOWNIK", new XElement("ZESPOL", zesp.element(nszesp + "NAZWA").Value), new XElement("NAZWISKO", prac.element(nsprac + "NAZWISKO").Value) ); foreach (var xelement in result) Console.WriteLine(xElement); i. W tym zapytaniu doszła klauzula join. Służy ona do łączenia wielu kolekcji w tym przypadku zesp.xml i prac.xml. Zwróć uwagę jak został zdefiniowany warunek połączeniowy. d. Zmodyfikuj odpowiednio funkcję Main i uruchom program.
e. Przykładowy wynik działania programu: Agregacja 8. Przedostatnie zadanie polega na przygotowaniu funkcji obliczającej sumę płacy podstawowej dla wszystkich pracowników. a. W tym celu skopiuj funkcję SimpleQuery i zmień jej nazwę na SimpleQuery_Aggregation b. Zastąp zapytanie LINQ następującym: var result = from prac in doc.descendants(nsprac + "ROW") group prac by prac.element(nsprac + "ID_ZESP").Value into grp select new sumplacapod = grp.sum(p => Decimal.Parse(p.Element(nsPrac + "PLACA_POD").Value)), avgplacapod = grp.average(p => Decimal.Parse(p.Element(nsPrac + "PLACA_POD").Value)), idzesp = grp.key ; c. Zmodyfikuj odpowiednio funkcję Main. Uruchom program. d. Przykładowy wynik działania programu:
e. Przykładowy kod: 9. Zmodyfikuj powyższą funkcję, tak by w postaci XML-a zwracała sumę płacy podstawowej i jej średnią pogrupowaną według zespołów wraz z nazwą zespołu. a. Przykładowy wynik działania programu: b. Przykładowe rozwiązanie: