• Zwiększ rozmiar czcionki
  • Domyślny  rozmiar czcionki
  • Zmniejsz rozmiar czcionki
Home Artykuły .NET LINQ to XML, cz. 3 - Zapytania - kontynuacja

LINQ to XML, cz. 3 - Zapytania - kontynuacja

Email Drukuj PDF
Ocena użytkowników: / 0
SłabyŚwietny 

W poprzednim artykule został przedstawiony sposób dostępu do węzłów w dokumencie Xml za pomocą Linq To Xml. Jedną z bardziej interesujących rzeczy było to, że w Linq to Xml nie używa się wyrażeń XPath. Zamiast tego używa się metod, które bardzo przypominają pojedyncze kroki w wyrażeniach XPath.

O ile w przypadku języka XPath kroki rozdzielone są znakiem '/' to w Linq To Xml wywołujemy poszczególne metody na wyniku zwróconym przez wywołanie metody funkcji (np. xDoc.Root.Elements("segment").Elements("wylot")). Oczywiście wybieranie węzłów z dokumentu Xmlowego to już cos ale w praktyce zazwyczaj zamiast wybierać z dokumentu wszystkie węzły o danej nazwie wybieramy tylko te które spełniają określone warunki. Przykładowo z następującego dokumentu (jest to ten sam dokument który był używany w poprzednich częściach cyklu):

<?xml version="1.0"?>
<wycieczka>
  <segment>
    <wylot kod-lotniska="SEA" data="2008-10-09T15:25Z">Seattle-Tacoma International</wylot>
    <przylot kod-lotniska="LHR" data="2008-10-10T02:20Z">London Heathrow</przylot>
  </segment>
  <segment>
    <wylot kod-lotniska="LHR" data="2008-10-10T05:35Z">London Heathrow</wylot>
    <przylot kod-lotniska="WRO" data="2008-10-10T10:05Z">Wroclaw Strachowice</przylot>
  </segment>
</wycieczka>

wybierzmy wszystkie elementy będące dziećmi elementu <segment>, których atrybut @kod-lotniska ma wartość "LHR". Zapytanie będzie wyglądać tak:

var elements =
from el in xDoc.Elements("wycieczka").Elements("segment").Elements()
where (string)el.Attribute("kod-lotniska") == "LHR"
select el;

Jak to działa? Pierwsza linia jest swoistego rodzaju iteratorem. Zmienna el będzie przyjmowała kolejne wartości ze zbioru węzłów wybieranych przez wyrażenie:

xDoc.Elements("wycieczka").Elements("segment").Elements()

(które swoją drogą powinno wyglądać znajomo z poprzedniego artykułu). W drugiej linii nakładamy warunek na zmienną el. W naszym przypadku jesteśmy zainteresowani tylko elementami których atrybut @kod-lotniska ma wartość "LHR". W ostatniej linii dodajemy do wynikowej kolekcji (var w przypadku zypytań Linqowych jest obiektem implementującym interfejs IEnumerable) el jeśli spełnia on warunek z linii drugiej ("select" w tym przypadku działa podobnie jak znane z języka C# "yield").

Oczywiście warunki w klauzuli "where" mogą być złożone. Przykładowo możemy poszerzyć warunek w powyższym zapytaniu tak aby dodatkow zawężyć wynik tylko do tych węzłów gdzie godzina wylotu lub przylotu jest poźniejsza niż czwarta. Oto przykład takiego zapytania:

var elements = 
from el in xDoc.Descendants()
where (string)el.Attribute("kod-lotniska") == "LHR" &&
((DateTime)el.Attribute("data")).Hour > 4
select el;

Zapytanie to jest interesujące jeszcze z jednego powodu - mianowicie okazuje się, że atrybut (obiekt klasy XAttribute) może być rzutowany zarówno do typu string jak i do typu DateTime. Odpowiadaja za to jawne (ang. explicit) operatory do konwersji typów. Są one zdefiniowane na klasach XAttribute oraz XElement. Więcej o jawnych operatorach kowersji typów można znaleźć w MSDN pod poniższym linkiem: http://msdn.microsoft.com/en-us/library/xhbhezf4.aspx. Lista typów dla których zdefiniowane są jawne operatory konwersji znajdują się tutaj dla klasy XElement http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.op_explicit.aspx a tutaj dla klasy XAttribute http://msdn.microsoft.com/en-us/library/system.xml.linq.xattribute.op_explicit.aspx. Na potrzeby tego kursu wystarczy chyba tylko wiedzieć, że jawne operatory kownersji umożliwiają rzutowanie nawet w przypadkach gdy typ docelowy nie jest typem pochodnym od typu źródłowego.

Zastosowanie jawnych operatorów konwersji typów przynosi następujące korzyści:

  • skraca zapytanie i zwiększa jego przejrzystość
  • pomaga kontrolować poprawność semantyczną dokumentu (oznacza to, że jeżeli rzutowana wartość nie jest zgodna z typem docelowym to zostanie wyrzucony wyjątek)
  • pomaga uniknąć wyjątków typu NullReferenceException związanymi z brakiem wyszukiwanego elementu lub atrybutu

Pierwsze dwa punkty są raczej jasne, natomiast nad trzeci chyba nieszczególnie. Załóżmy przez chwilę, że zamiast używać operatora konwersji będziemy "dobierać" się do wartości elementu używając właściwości .Value. Nasze zapytanie wyglądałoby wtedy następująco:

var elements = 
from el in xDoc.Elements("wycieczka").Elements("segment").Elements()
where el.Attribute("kod-lotniska").Value == "LHR"
select el;

Zapytanie działa ale co by się stało gdyby atrybutu @kod-lotniska nie było? W takim przypadku wywołanie el.Attribute("kod-lotniska") zwróciłoby wartość null. W efekcie próba pobrania wartości atrybutu za pomocą właściwości .Value skończyłaby się wyjątkiem NullReferenceException. Operator konwersji pozwala uniknąć tego rodzaju sytuacji. Jest to bardzo poręczne, ponieważ w praktyce często można spotkać sie z dokumentami Xmlowymi zawierającymi węzły lub atrybuty, które mogą występować opcjonalnie.

Linq to Xml dopuszcza stosowanie zmiennych w zapytaniach. Załóżmy, że chcemy wybrać z naszego przykładowego dokumentu wszystkie elementy <wylot> i <przylot>, których data jest taka sama jak data ostatniego elementu <przylot>. Przykładowe zapytanie może wyglądać tak:

var elements = 
from el in xDoc.Elements("wycieczka").Elements("segment").Elements()
let dataPrzylotu =
  (DateTime)xDoc.Descendants("przylot").Last().Attribute("data")
where ((DateTime)el.Attribute("data")).ToShortDateString() ==
  dataPrzylotu.ToShortDateString()
select el;

W zapytaniu, data przylotu ostatniego segmentu jest zapisywana w zmiennej dataPrzylotu. Zmienna ta jest później używana w klauzuli where do porównania daty aktualnie przetwarzanego elementu. Linq daje też programiście możliwość tworzenia podzapytań (ang. subqueries). Oto przykładowe zapytanie zawierające podzapytanie:

var elements = from segmenty in xDoc.Descendants("segment")
         from przyloty in segmenty.Elements("przylot")
               select przyloty;

Mniej złożone dokumenty nie wymagają zazwyczaj stosowania podzapytań (tak też jest w przypadku naszego przykładowego dokumentu - dlatego powyższy przykład zapytania jest troche "naciągany").

Artykuł zamieszczono dzięki uprzejmości xml.com.pl

Poprawiony: poniedziałek, 17 sierpnia 2009 09:23  

Dodaj swój komentarz

Imię:
Adres e-mail:
Strona WWW:
Treść (możesz używać HTML):
JoomlaWatch Stats 1.2.9 by Matej Koval

Użytkownicy

Naszą witrynę przegląda teraz 1 gość