Výzva - doménové objekty a jejich naplnění
Pokračování výzvy z webtrhu pokračuje, tentokráte povídáním o tom, jak jsem si vytvořil doménové objekty pro uchování dat a provedl jejich naplnění.
Doménové objekty
V příspěvku věnovaném definici zdroje dat jsem končil v okamžiku, kdy jsem byl schopen pomocí REST volání získat xml zdroj dat. Z následné diskuze na webtrhu vyplynulo, že Petra zrovna neholduje XSL šablonám a přesto by byla ráda, pokud by si mohla v případě potřeby upravit výsledné HTML, které bude publikovat na web. O tom, jak jsem se tohoto problému zhostil v jednom z příštích článků. Co jsem však potřeboval, bylo mít naplněné objekty, které budou sloužit pro držení dat.
Ze vstupního xml souboru jsem si tak definoval několik elementů, které stojí za to mít uchované, a které se budou moci následně zobrazovat a publikovat na webu. Obdobně bych postupoval i se vstupním xml souborem produktů, zde jsem se tedy zaměřil na knihy. Mnou vytvořené třídy tedy představovaly primitivní POCO (Plain Old CLR Object) objekty s definicí vlastností, které odpovídaly dostupným elementům.
- Book - ASIN, ISBN, NumberOfPages, PublicationDate, Publisher, Title, Authors, Reviews
- Author - FullName
- Review - Source, Content
Linq to Xml
Poté, co jsem měl nadefinovány tyto jednoduché objekty bylo je třeba vytvořit a naplnit daty. K tomu jsem s úspěchem použil nových objektů, které nabízí verze .net frameworku 3.5. A to konkrétně objektů XElement ve spojení s LINQ to XML.
Stažení dat tak bylo otázkou jednoho jediného řádku:
XElement x = XElement.Load(url);
kde proměnná url představovala doplněnou adresu o proměnné parametry, které jsem představil v minulém článku.
Pokud si nyní říkáte, jak složité je naplnit POCO objekty vstupnímy daty a získat tak seznam dostupných knih poskytovaných webovou službou amazonu, je to přesně 23 řádků kódu, z čehož je ještě několik řádků věci vizuálního formátování.
A zde je celé kouzlo, kterým se provede instancování objektů a jejich naplnění
var result = from book in x.Descendants(ns + "Item") let attrs = book.Element(ns + "ItemAttributes") let revs = book.Element(ns + "EditorialReviews") select new Book() { ASIN = (string)book.Element(ns + "ASIN"), ISBN = attrs.Element(ns + "ISBN") != null ? (string)attrs.Element(ns + "ISBN") : string.Empty, NumberOfPages = (int?)attrs.Element(ns + "NumberOfPages") ?? 0, PublicationDate = (DateTime)attrs.Element(ns + "PublicationDate"), Publisher = (string)attrs.Element(ns + "Publisher"), Title = (string)attrs.Element(ns + "Title"), Authors = new List<Author>(from a in attrs.Elements(ns + "Author") select new Author() { FullName = (string)a }), Reviews = revs == null ? new List<Review>() : new List<Review>(from r in revs.Descendants(ns + "EditorialReview") select new Review() { Content = (string)r.Element(ns + "Content"), Source = (string)r.Element(ns + "Source") }) };
Přítelem je let
Teď to možná zní velice divně, ale pokud se blíže podíváte na zdrojový kód uvedený výše zjistíte, že jsem použil magické slůvko let, kterým jsem si vložil do dotazu další proměnné, které používám. První proměnná attrs je pouze pro usnadnění práce, u druhé to také tak může vypadat, ale má to i hlubší význam. Jak se můžete u některých knih přesvědčit, ne všechny obsahují recenzi. Pokud bych poté chtěl naplnit vlastnost Reviews přímo a to odkazem na element EditorialReviews obdržel bych výjimku, konkrétně NullReferenceException, a to se mi zrovna nehodí. Proto jsem si odkaz na tento element uschoval do proměnné a v okamžiku potřeby přirazení do vlastnosti Reviews jej kontroluji a případně vytvořím pouze prázdný generický List<T>.
Scházející elementy
Do obdobné situace jsem se potom dostal v případě, že jsem se snažil naplnit vlastnosti, avšak ve zdrojovém xml scházel požadovaný element. Příkladem budiž třeba scházející ISBN kód nebo nevyplněný počet stránek knihy. V případě ISBN jsem toto vyřešil pomocí ternárního operátoru, v případě počtu stránek jsem potom opět využil možností přetypovat výsledek na nullable type typu int a pomocí operátoru ?? případně vrátit hodnotu 0.
Jak by to vypadalo
Tento krok by se nejspíše odehrál společně s krokem prvním a to při definici zdroje dat. Určili bychom si, které atributy ze vstupního souboru nás budou zajímat, jakým způsobem jsou tyto atributy provázány, stejně tak si řekli, které atributy jsou povinné a které volitelné. Pokud by k vstupnímu xml souboru bylo definováno xsd schéma bylo by to jistě jednodušší.
Časový odhad
Definice doménových objektů a jejich naplnění byl již poměrně rychlý krok. Co bylo spíše nepříjemné je definování vlastností, v čemž jsem s radostí uvítal automatické property. Dalším krokem pak bylo poskládání LINQ dotazu na zdrojové xml. Celkový čas byl odhadem 25 minut + 10 minut testování na vzorku několika stovek záznamů. V případě produktů, které chtěla Petra sledovat by byl čas závislý na tom, kolik atributů by bylo nutné sledovat a mít k nim uložená data.