Jednoduchá práca s XML dokumentom v C# (vďaka nástroju xsd.exe priamo od Microsoftu)

Publikoval Michal Kočí dňa 18.10.2004 o 01:37 v kategórii .Net

Program xsd.exe je súčasťou .Net Frameworku a zvláda (okrem iného) nasledovné činnosti pre uľahčenie programovania aplikácií pracujúcich s XML súbormi, a keďže XML sa v poslednej dobe stále viac prediera do reálneho používania čoraz viac, myslím si, že stojí za to, čo-to o tomto programe vedieť.

1. Vygenerovanie XML schémy (XSD) z XML súboru

xsd.exe organizacia.xml

Ako každý nástroj pre automatické generovanie, ani tento bohužial nie vždy pracuje tak ako by sme si predstavovali. Ak napríklad v XML súbore použijete element, ktorého hodnotou je vždy číslo, tento nástroj tento typ nesprávne navrhne ako reťazec. To však až tak nevadí, lebo výsledný XSD súbor si môžete po vygenerovaní zmodifikovať a upraviť ho presne pre Vaše potreby. Xsd.exe Vám vygeneruje aspoň kostru.

2. Vygenerovanie XML schémy (XSD) z triedy nachádzajúcej sa v skompilovanej assembly

xsd.exe System.Drawing.dll /type:Color

Trieda, pre ktorú chcete generovať XML schému musí mať defaultný verejný konštruktor, čiže z assembly System.Drawing.dll môžete generovať schému pre triedu Color, ale nie napríklad pre triedu Pen.

3. Vygenerovanie tried pre prácu s dátami v XML súbore (triedy používajúce serializáciu)

xsd.exe organizacia.xsd /classes

4. Vygenerovanie triedy zdedenej z triedy DataSet pre prácu s dátami v XML súbore

xsd.exe organizacia.xsd /dataset

Túto aplikáciu púšťate z príkazového riadku a ovládate ju parametrami. Nimi môžete napríklad určiť, pre ktorý jazyk chcete generovať triedy (C#, Visual Basic.Net, ...). Parametrov je viacej, ich detailný popis sa nachádza napríklad v MSDN

Situácií, kedy môže byť vhodné použiť tento nástroj je niekoľko:

  • Váš partner (partnerská firma, ...) chce s Vami komunikovať vo formáte XML a nechcete si triedu vediacu spracovať ich XML dokument generovať ručne.
  • Chcete si z vašej aplikácie ukladať nastavenia do XML súboru a nechcete (z ľubovoľného dôvodu) nastavenia ukladať do app.config (web.config v prípade ASP.Net aplikácie) súboru.
  • Chcete spraviť malú databázu (rádovo niekoľko desiatok, alebo stoviek záznamov) a nechcete dáta ukladať do seriózneho databázového stroja (napríklad MS SQL), ale iba do súboru (v tomto prípade XML).

Ja som si na ukážku vybral dôvod číslo 3, teda vytvorenie malej databázy, ktorá sa bude dáta ukladať do XML. Na príkladoch ukážem použitie vygenerovaných tried (serializácia) ako aj použitie triedy DataSet.

Uvažovať budeme s nasledovnou hypotetickou situáciou: Potrebujeme evidovať zamestnancov našej firmy, o každom nám stačí evidovať jeho meno, priezvisko a plat. Každý zamestnanec navyše pracuje na nejakom oddelení o ktorom budeme evidovať iba jeho meno. Rozhodneme sa napríklad, že názov oddelenia budeme ukladať ako atribút, údaje o zamestnancovi ako elementy. Viac si príklad nebudeme komplikovať. Náš XML súbor by teda mohol vyzerať nasledovne:

<?xml version="1.0" encoding="utf-8" ?>
<organizacia xmlns="http://tempuri.org/organizacia.xsd">
 <oddelenie nazov="IT">
  <zamestnanec>
   <meno>Jozef</meno>
   <priezvisko>Mokrý</priezvisko>
   <plat>17000</plat>
  </zamestnanec>
  <zamestnanec>
   <meno>Igor</meno>
   <priezvisko>Rýchly</priezvisko>
   <plat>17500</plat>
  </zamestnanec>
 </oddelenie>
 <oddelenie nazov="Právne">
  <zamestnanec>
   <meno>Edita</meno>
   <priezvisko>Kováčová</priezvisko>
   <plat>31700</plat>
  </zamestnanec>
  <zamestnanec>
   <meno>Peter</meno>
   <priezvisko>Novák</priezvisko>
   <plat>29000</plat>
  </zamestnanec>
  <zamestnanec>
   <meno>Filip</meno>
   <priezvisko>Krátky</priezvisko>
   <plat>19500</plat>
  </zamestnanec>
 </oddelenie>
</organizacia>

Keď máme XML súbor hotový, v medzi kroku si vygenerujeme XSD súbor, z ktorého si následne budeme generovať triedy, ktoré chceme používať v našej aplikácii:

xsd.exe organizacia.xml

XSD si ešte zmodifikujeme a to tak, že typ elementu plat zmeníme z typu string

<xs:element name="plat" type="xs:string" minOccurs="0" />

na typ int

<xs:element name="plat" type="xs:integer" minOccurs="0" />

Ako prvé si vygenerujeme triedy, ktoré používajú serializáciu:

xsd.exe organizacia.xsd /classes

Ak si pozrieme kód, ktorý nám xsd.exe vygeneroval, zistíme, že nám vygeneroval tri triedy. S popisom začnem z dola a budem sa prebojovávať postupne nahor. Na spodu celej hierarchie musí byť logicky trieda, ktorá nám umožní uchovávať údaje o zamestnancovi (jeho meno, priezvisko a plat). Touto je vygenerovaná trieda organizaciaOddelenieZamestnanec. Keďže zamestnanci sú organizovaní do oddelení, nástroj vygeneroval triedu organizaciaOddelenie, ktorá okrem toho, že si uchováva svoj názov (t.j. názov oddelenia), uchováva si aj pole zamestnancov. A na vrchu hierarchie je trieda oragnizacia, ktora uchováva pole oddelení.

Čo stojí za povšimnutie je, že nástroj vygeneruje public vlastnosti, čiže ich negeneruje ako private a k nim príslušné prístupové metódy (get a set). Toto však v tomto prípade nevadí, keďže objektom nejdeme pridávať ďaľšie metódy. Navyše by to ani nebolo vhodné, pretože pri prípadnej zmene XML súboru a teda následnom pregenerovaní tried by sme všetky naše zmeny stratili. Čiže, je vhodné uspokojiť sa s výsledkom práce tohto nástroja, alebo nástroj vôbec nepoužíť.

Ďalej si všimnite, že nástroj automaticky pridá k triedam atribúty potrebné pre správnu serializáciu objektov. S týmto súvisí ďaľšia vec, ktorá mne nie je až taká jasná, a to, že zamestancova vlastnosť plat je typu string a to, že sa jedná o celočíselný element je označené iba atribútom. Týmto strácame kontrolu nad hodnotami tejto vlastnosti v čase kompilácie. Škoda.

Ak by sme chceli súbor načítať, kód by mohol vyzerať nasledovne:

FileStream fs = new FileStream(@"c:\organizacia.xml", FileMode.Open);
XmlSerializer xmls = new XmlSerializer(typeof(organizacia));
organizacia org = (organizacia) xmls.Deserialize(fs);
fs.Close();

Pridať oddelenie je trochu zložitejšie a pri trochu vačšom počte oddelení môže byť táto akcia aj časovo náročná. Problém totiž je, že nástroj vám vygeneruje pre zoznam oddelení pole. Akonáhle stanovíte veľkosť poľa, nemôžete už jeho veľkosť meniť. Kedže v našom prípade sa veľkosť poľa stanovuje pri deserializácii, pri pridávaní oddelenia musíme vytvoriť nové pole oddelení s počtom prvkov o jeden vyšším ako je stávajúca veľkosť (za predpokladu, že ideme pridať iba jedno oddelenie), a do tohto zväčšeného poľa prekopírovať stávajúce oddelenia. Obdobné by platilo aj pri pridávaní zamestnanca.

organizaciaOddelenie[] oddnew = new organizaciaOddelenie[org.Items.Length+1];
org.Items.CopyTo(oddnew, 0);
org.Items = oddnew;
org.Items[org.Items.Length-1] = new organizaciaOddelenie();
org.Items[org.Items.Length-1].nazov = "Ekonomické";

Prechádzanie jednotlivých oddelení a/alebo zamestnancov je triviálne, použiť naň môžeme alebo cyklus for, alebo cyklus foreach.

foreach(organizaciaOddelenie odd in org.Items)
{
    Console.WriteLine("Oddelenie: {0}", odd.nazov);
    foreach(organizaciaOddelenieZamestnanec zam in odd.zamestnanec)
    {
        Console.WriteLine("\tZamestnanec: {0} {1} s platom {2}",
            zam.meno, zam.priezvisko, zam.plat);
    }
}

Ak chceme uložiť súbor, kód môže vyzerať takto:

FileStream fs = new FileStream(@"c:\organizacia2.xml", FileMode.Create);
XmlSerializer xmls = new XmlSerializer(typeof(organizacia));
xmls.Serialize(fs, org);
fs.Close();

Jednoduché. Jedná sa o jednoduché programovanie, nič špeciálne, iba to, že súbor načítame a ukladáme s použitím serializácie (objekt typu XMLSerializer z namespace System.Xml.Serialization). Ako však vyplýva z vyššie uvedených faktov (použitie poľa namiesto kolekcie pre zoznam oddelení, zamestnancov, ...), použitie týchto tried nám síce na jednej strane uľahčuje prácu so samotným XML dokumentom, na druhej strane nám ju opäť trochu sťažuje. Tieto triedy sa mne osobne javia vhodné v prípade, že by som XML dokument chcel iba načítavať ale nie v ňom robiť nejaké zmeny, prípadne robiť v ňom zmien minimum. 

Druhým spôsobom uľahčenia práce je vygenerovanie si triedy ktorá dedí z triedy DataSet. Pri postupe týmto smerom môžeme už na začiatku očakávať jasné výhody a nevýdody tohto riešenia. Medzi výhody nesporne patrí bohatstvo metód zdedených z triedy DataSet, menej kódu pri pridávaní oddelenia alebo zamestnanca a v neposlednom rade možnosť šikovného nabindovania na nejaký grid (napríklad DataGrid, alebo grid dodaný od tretej strany) zobrazujúci dáta (prípadne povoľujúci ich editáciu priamo užívateľovi). Medzi nevýhodami sa jedná najmä o réžiu spojenú s objektom typu DataSet, ktorá by v prípade potreby iba čítania XML súboru mohla byť zbytočne privysoká.

Opäť začnem popisom vygenerovaných tried, tento raz zhora nadol. Na vrchu stojí opäť trieda organizacia, ktorá tentoraz dedí z triedy DataSet. Táto obsahuje množinu ďaľších tried, ktoré nám uľahčujú celkovú prácu a dajú sa podeliť do logických skupín. Zaostrím na dve skupiny. Prvou je skupina dátových tabuliek (triedy zdedené z DataTable). Tieto sa nám vygenerujú dve, jedna pre oddelenia a druhá pre zamestnancov. Druhou skupinou je skupina riadkov (triedy zdedené z DataRow), opäť jedna pre riadok s informáciami o oddelení, druhá pre riadok s informáciami o zamestnancovi. Pri generovaní týchto tried je k nám nástroj trochu milší, konkrétne v tom, že vlastnosť plat je tentokrát typu long, narozdiel od prechádzajúceho prístupu, kde nám vygeneroval typ string.

Podrobnému preskúmaniu vygenerovaných tried sa vyhnem, lebo pri práci s týmto nástrojom a začatí jeho používania sa tak či tak raz pozriete na zúbky vygenerovaným triedam, ktorých zdrojový kód je oveľa vačší, ako v predchádzajúcom prípade (v prípade nechania si vygenerovania tried pre použitie so serializáciou).

Radšej sa vrhnem na príklady použitia. Na začiatku treba samozrejme načítať XML súbor:

organizacia orgds = new organizacia();
orgds.ReadXml(@"c:\organizacia.xml");

Prípadne po jeho načítaní môžem tento rovno nabindovať na DataGrid:

organizacia orgds = new organizacia();
orgds.ReadXml(@"c:\organizacia.xml");
dgOrganizacia.DataSource = orgds;

Prípadne môžem nabindovať priamo tabuľku (DataTable) oddelení:

dgOrganizacia.DataSource = orgds.oddelenie;

Na konci práce budete musieť (respektíve, pravdepodobne budete chcieť) DataSet opäť uložiť:

orgds.WriteXml(@"c:\organizacia2.xml");

Prípadne, ak som si DataSet nabindoval na DataGrid a chcem uložiť aj zmeny, ktoré vykonal užívateľ:

organizacia orgds = (organizacia) dgOrganizacia.DataSource;
orgds.WriteXml(@"c:\organizacia2.xml");

Opäť jednoduché. Toto mi môže stačiť. Ak mi išlo iba o to, aby si užívateľ v nejakej forme vedel zobraziť informácie o zamestnancoch a oddeleniach a prípadne aby ich vedel pridávať a odoberať, docielili sme to piatimi riadkami kódu. Jednoduchšie to už snáď nejde.

Poslednú vec, ktorú ukážem, je pridanie oddelenia priamo z kódu. Ostatne, kto pracoval s DataSet-om, akákoľvek manipulácia s dátami pri tomto prístupe mu bude hneď jasná:

organizacia.oddelenieRow odd = orgds.oddelenie.NewoddelenieRow();
odd.nazov = "Ekonimické";
orgds.oddelenie.Rows.Add(odd);

Toľko jednoduché príklady, na ktorých som snáď ukázal, ako nám nástroj xsd.exe dokáže uľahčiť prácu. Vedel by som si predstaviť aj o trochu lepší výsledok vygenerovaných tried, ale ako sa vraví, darovanému koňovi netreba pozerať na zuby.

Čo ma trochu (nemilo) prekvapilo je, že som tento nástroj nenašiel v .Net Frameworku 2.0 Beta 2, dúfam že iba preto, že ho Microsoft ešte nestihol dokončiť a čakajú nás nejaké vylepšenia, a nie preto, že by ho Microsoft prestal s novou verziou Frameworku dodávať...

Mohlo by ťa tiež zaujímať

Páčil sa ti príspevok?

Zdieľaj príspevok alebo si ho odlož na neskôr

Sleduj ma

Ak nechceš premeškať príspevky ako je tento, sleduj ma na Twitteri, alebo ak máš RSS čítačku, môžeš sledovať môj RSS kanál.

Komentáre

K tomuto článku nie su pridané žiadne komentáre.

Pridať komentár

Máš niečo zaujímavé povedať k článku? Pridaj to k článku ako komentár. Spam, reklamu alebo inak nerelevantné komentáre okamžite mažem.