Lekce 6 - Úvod do XML a zápis SAXem v C# .NET
V předešlém cvičení, Řešené úlohy k 1.-5. lekci práce se soubory v C# .NET, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
Dnes se v C# .NET tutoriálu zaměříme na formát XML. Nejprve si ho popíšeme, poté si ukážeme třídy, které nám .NET framework poskytuje k jeho čtení a zápisu.
Formát XML
XML (eXtensible Markup Language) je značkovací jazyk vyvinutý organizací W3C, která má na starosti webové standardy. XML je velmi univerzální a je podporován řadou jazyků i aplikací. Slovo extensible (rozšiřitelný) označuje možnost vytvořit si pomocí XML vlastní jazyk, kterým je například XHTML pro tvorbu webových stránek.
XML je jazyk samopopisovací. Má takovou strukturu, že poznáme, co která hodnota označuje. Zatímco u CSV se můžeme jen domnívat, co je ta třetí osmička, v XML by nám bylo hned jasné, že je to například počet článků uživatele. Nevýhodou formátu je samozřejmě jeho větší velikost, což nám však ve většině případů nevadí. Osobně téměř vždy při výběru formátu sahám po XML.
XML se hodí například k uložení:
- konfigurace programu,
- hiscore hráčů hry,
- menší databáze uživatelů.
Díky XSD schématům ho můžeme jednoduše zvalidovat a předejít chybám při běhu programu.
XML je možné zpracovávat hned několika způsoby. Obvykle jednoduchým kontinuálním čtením/zápisem nebo pomocí objektové struktury DOM. Došlo to tak daleko, že nám některé nástroje včetně .NET knihoven umožňují s XML dokumentem pracovat jako s databází a volat nad ním SQL dotazy. Asi si dokážeme představit, jak to usnadní práci. Dalším jazykem k dotazování se nad XML soubory je například XPath.
XML konkuruje JSON, který je jednodušší, ale méně populární v obchodních aplikacích. Ten na rozdíl od XML umožňuje snadné logování na konec souboru bez načtení celého dokumentu.
XML se velmi často používá k výměně dat mezi rozdílnými systémy například desktopovou aplikací a webovou aplikací na serveru. Proto, jak již bylo zmíněno, existuje pro něj mnoho knihoven a každý nástroj ho zná a umí s ním pracovat. S tím souvisejí webové služby, SOAP a podobně, čímž se však nyní nebudeme zabývat.
Porovnání formátů XML a CSV
V lekci Uložení objektů do CSV v C# .NET - Dokončení jsme si uložili seznam uživatelů do souboru CSV. Ukládali jsme jméno, věk a datum registrace. Hodnoty byly zasebou, oddělené středníky. Každý řádek představoval jednoho uživatele. Obsah souboru vypadal tedy asi takto:
Pavel Slavík;22;21.3.2000 Jan Novák;31;30.10.2012
Nezasvěcený z toho nic moc nepozná, že? Udělejme si nyní ekvivalent souboru ve formátu XML:
<?xml version="1.0" encoding="UTF-8" ?> <uzivatele> <uzivatel vek="22"> <jmeno>Pavel Slavík</jmeno> <registrovan>21.3.2000</registrovan> </uzivatel> <uzivatel vek="22"> <jmeno>Jan Novák</jmeno> <registrovan>30.10.2012</registrovan> </uzivatel> </uzivatele>
Nyní každý vidí, co je v souboru uloženo. Věk jsme zde uložili jako atribut jen proto, abychom si ukázali, že je XML umí. Jinak by mohl být jako element spolu se jménem a registrován. Jednotlivým prvkům se říká elementy. HTML, o kterém se dozvíme více v kurzu HTML5 od A do Z, vychází ze stejných základů jako XML. Elementy jsou většinou párové, tedy otevírací, poté následuje hodnota a uzavírací s lomítkem. Elementy mohou obsahovat další elementy, struktura je tedy stromová. Díky tomu není problém do jediného XML dokumentu uložit celou hierarchii objektů.
Na začátku souboru je XML hlavička. Aby byl dokument validní, musí obsahovat právě 1 kořenový element, zde element uzivatele, ve kterém jsou zabaleny další elementy. Atributy se píší za název atributu do uvozovek.
Určitě vidíme, že soubor nám nabobtnal, to je daň za to, že vypadá hezky. Kdyby neměl uživatel vlastnosti jen tři, ale například 30, bylo by vidět, jak CSV formát přestává stačit.
Osobně se s přibývajícími zkušenostmi stále více přikláním k řešením, která jsou přehledná a jednoduchá, i když třeba zabírají více paměti, a to nejen v souborech, ale i ve zdrojovém kódu. Nic není horšího, než když programátor přijde za rok ke svému programu a vůbec neví, co je ten osmý parametr v CSV, kde je na řádku 100 nějakých čísel. Nebo co je to pětirozměrné pole, které je sice hrozně rychlé, ale kdyby si místo něj postavil objektovou strukturu, nemusel by nyní psát program odznovu.
XML v .NET
Budeme se zde věnovat dvou základním přístupům ke XML:
- Kontinuálnímu přístupu takzvaný SAX parser.
- Objektový přístup (DOM).
Opět máme v .NET frameworku více způsobů, jak s XML pracovat. Některé jsou zastaralé a přítomné jen z důvodu zpětné kompatibility.
Parsování XML přes SAX
SAX (Simple API for XML)
je vlastně jednoduchá nádstavba čtečky textových
souborů. Zápis je poměrně jednoduchý, postupně zapisujeme
elementy a atributy tak, jak jdou v souboru za sebou. Stromovou strukturu zde
tedy ignorujeme. .NET nám poskytuje třídu XmlWriter
, která nás
odstiňuje od toho, že je XML textový soubor. Pracujeme pouze s elementy,
přesněji uzly, více dále.
Čtení probíhá podobně, jako zápis. XML tedy čteme jako textový
soubor, řádek po řádku, odshora dolů. SAX nám však nedává řádky, ale
postupně takzvané uzly XMLNode
, na které při čtení postupně
naráží. Uzel může být element, atribut nebo hodnota. V cyklu uzly
dostáváme tak, jak jdou v souboru zasebou. Ke čtení použijeme třídu
XmlReader
. Obě třídy jsou ve jmenném prostoru
System.Xml
.
Výhodou SAX je vysoká rychlost a paměťová nenáročnost. Nevýhody poznáme, až tento přístup dále srovnáme s objektovým přístupem DOM v následujících lekcích.
Zápis XML
Pojďme si vytvořit jednoduché XML. Využijeme k tomu výše uvedený
příklad s uživateli. S třídou Uzivatel
jsme pracovali v
minulých lekcích, pro jistotu si ji zde ještě jednou uvedeme. Založme si
novou konzolovou aplikaci XmlSaxZapis
a přidejmne si třídu
Uzivatel
:
class Uzivatel { public string Jmeno { get ; private set; } public int Vek { get; private set; } public DateTime Registrovan { get; private set; } public Uzivatel(string jmeno, int vek, DateTime registrovan) { Jmeno = jmeno; Vek = vek; Registrovan = registrovan; } public override string ToString() { return Jmeno; } }
Metoda Main()
Kód budeme pro jednoduchost psát do metody Main()
, pouze si
vyzkoušíme funkčnost SAXu. Z minulých lekcí víme, jak se aplikace píší
správně objektově.
Do using
přidejme System.Xml
.
XmlWriter
vytváříme pomocí tovární (statické) metody
Create()
. Jde to i jinak, ale tento způsob je nejvýhodnější.
Objekt bude opět obalen v bloku using
. Do XML můžeme uložit
samozřejmě i jen jeden objekt například nastavení. My si zde ukážeme
uložení seznamu několika objektů.
Jako první si vytvoříme testovací list uživatelů:
List<Uzivatel> uzivatele = new List<Uzivatel>(); uzivatele.Add(new Uzivatel("Pavel Slavík", 22, new DateTime(2000, 3, 21))); uzivatele.Add(new Uzivatel("Jan Novák", 31, new DateTime(2012, 10, 30))); uzivatele.Add(new Uzivatel("Tomáš Marný", 16, new DateTime(2011, 1, 12)));
Již máme co zapisovat. Budeme chtít, aby byl XML výstup hezky
naformátovaný a odsazený dle své stromové struktury. Toto nastavení
bohužel není defaultní a proto ho musíme vynutit předáním instance
třídy XmlWriterSettings
. Té nastavíme vlastnost
Indent
(zanořovat) na true
:
XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true;
Hotovo. Nyní vytvoříme instanci třídy XmlWriter
pomocí
tovární metody Create()
. Budeme pracovat v bloku
using
. Instanci jako parametry předáme cestu k souboru a
nastavení:
using (XmlWriter xw = XmlWriter.Create(@"soubor.xml", settings)) { }
Zápis dat do souboru
Pojďme se pustit do samotného zápisu. Nejprve zapíšeme hlavičku dokumentu:
xw.WriteStartDocument();
Dále, jak již víme, musí následovat kořenový element, ve kterém je
celý zbytek XML obsažen. K zapisování elementů máme metody
WriteStartElement()
a WriteEndElement()
. První bere v
parametru název elementu, který otevíráme. Druhá metoda pozná název
otevřeného elementu sama z kontextu dokumentu a parametry tedy nemá. Otevřme
kořenový element, v našem případě element uzivatele:
xw.WriteStartElement("uzivatele");
Nyní se dostáváme k zápisu jednotlivých uživatelů. Ten bude přítomen
ve foreach
cyklu.
Zápis hodnoty do elementu provedeme pomocí metody
WriteValue()
, parametrem je zapisovaná hodnota. Obdobně můžeme
elementu přidat atribut metodou WriteAttributeString()
, jejíž
parametry jsou název atributu a jeho hodnota. Hodnota je vždy typu
string
, čili v našem případě musíme věk na
string
převést. Cyklus a zápis elementu uživatel, zatím
ještě bez vnořených elementů, bude vypadat takto:
foreach (Uzivatel u in uzivatele) { xw.WriteStartElement("uzivatel"); xw.WriteAttributeString("vek", u.Vek.ToString()); xw.WriteEndElement(); }
Do programu připíšeme ještě jeden EndElement()
k uzavření
kořenového elementu a EndDocument()
k ukončení celého
dokumentu. Podobně jako u textových souborů musíme i zde vyprázdnit buffer
metodou Flush()
. Celý kód programu nyní vypadá takto:
List<Uzivatel> uzivatele = new List<Uzivatel>(); uzivatele.Add(new Uzivatel("Pavel Slavík", 22, new DateTime(2000, 3, 21))); uzivatele.Add(new Uzivatel("Jan Novák", 31, new DateTime(2012, 10, 30))); uzivatele.Add(new Uzivatel("Tomáš Marný", 16, new DateTime(2011, 1, 12))); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; using (XmlWriter xw = XmlWriter.Create(@"soubor.xml", settings)) { xw.WriteStartDocument(); xw.WriteStartElement("uzivatele"); foreach (Uzivatel u in uzivatele) { xw.WriteStartElement("uzivatel"); xw.WriteAttributeString("vek", u.Vek.ToString()); xw.WriteEndElement(); } xw.WriteEndElement(); xw.WriteEndDocument(); xw.Flush(); }
Program si zkusíme spustit a ujistíme se, že vše funguje. Výstup
programu vidíme ve složce s programem na cestě
.../bin/debug/soubor.xml
:
<?xml version="1.0" encoding="utf-8"?> <uzivatele> <uzivatel vek="22" /> <uzivatel vek="31" /> <uzivatel vek="16" /> </uzivatele>
Vidíme, že SAX poznal, že v elementu uzivatel není kromě atributu
žádná hodnota a tak tag vyrenderoval jako nepárový. Nyní vložíme do
elementu uzivatel
dva další elementy, přesněji jeho vlastnosti
jméno a datum registrace:
xw.WriteStartElement("jmeno"); xw.WriteValue(u.Jmeno); xw.WriteEndElement(); xw.WriteStartElement("registrovan"); xw.WriteValue(u.Registrovan.ToShortDateString()); xw.WriteEndElement();
Ani jeden element v sobě neobsahuje další elementy ani atributy. Takovéto
elementy pouze s textovou hodnotou můžeme zapsat s pomocí jediné metody
WriteElementString()
, jejíž atributy jsou název elementu a
hodnota, kterou má obsahovat:
xw.WriteElementString("jmeno", u.Jmeno); xw.WriteElementString("registrovan", u.Registrovan.ToShortDateString());
Oba zápisy jsou tedy ekvivalentní.
Ten kratší vložíme do místa zápisu elementu uzivatel, tedy mezi jeho
StartElement()
a EndElement()
. Pro jistotu si ještě
uveďme kompletní kód části s cyklem:
foreach (Uzivatel u in uzivatele) { xw.WriteStartElement("uzivatel"); xw.WriteAttributeString("vek", u.Vek.ToString()); xw.WriteElementString("jmeno", u.Jmeno); xw.WriteElementString("registrovan", u.Registrovan.ToShortDateString()); xw.WriteEndElement(); }
A máme hotovo.
V příští lekci, Čtení XML SAXem v C# .NET, si načteme XML soubor s několika uživateli pomocí SAX parseru, třídou XmlReader. Ze souboru vytvoříme instance a přidáme je do listu.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 813x (27.83 kB)
Aplikace je včetně zdrojových kódů v jazyce C#