7. díl - Čtení XML SAXem v C#

C# .NET Práce se soubory Čtení XML SAXem v C#

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Úvod do XML a zápis SAXem, jsme si představili formát XML a ukázali si, jak pomocí SAXu vytvořit jednoduché XML. Nyní na minulý C# .NET tutoriál navážeme a napíšeme si proces opačný, tedy načtení XML souboru s uživateli a sestavení příslušné objektové struktury (listu uživatelů).

Pro úplnost si opět uvedeme náš XML soubor soubor.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="31">
    <jmeno>Jan Novák</jmeno>
    <registrovan>30.10.2012</registrovan>
  </uzivatel>
  <uzivatel vek="16">
    <jmeno>Tomáš Marný</jmeno>
    <registrovan>12.1.2011</registrovan>
  </uzivatel>
</uzivatele>

A naši třídu Uzivatel.cs:

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;
        }

}

Založme si nový projekt, půjde opět o konzolovou aplikaci. Pojmenujeme ji XmlSaxCteni a do složky bin/debug nakopírujeme náš XML soubor. K projektu připojíme také třídu Uzivatel. Uživatele budeme chtít načíst do nějaké kolekce, vytvořme si tedy prázdný list uzivatele. Kód budeme kvůli jednoduchosti psát do metody Main(), jak to udělat dobře objektově jsme si v této sekci již ukazovali.

List<Uzivatel> uzivatele = new List<Uzivatel>();

Čtení XML přes SAX

Ke čtení XML přes SAX nám .NET framework poskytuje třídu XmlReader. Pojďme si vytvořit její instanci. Jako tomu bylo u třídy XmlWriter, i zde k tomu využijeme tovární metody Create(), jejímž parametrem je název souboru. Nezapomeneme do using připsat System.Xml. Vše bude v bloku using, který se nám postará o uzavření souboru:

using (XmlReader xr = XmlReader.Create(@"soubor.xml"))
{
}

Připravíme si pomocné proměnné pro vlastnosti uživatele. Nemůžeme ukládat přímo do instance, protože vlastnosti jsou read-only. Druhou možností může být povolit modifikaci zvenčí, tím ale ztrácíme část zapouzdření. Vlastnosti naplníme výchozími hodnotami, které tam zůstanou v případě, že daná hodnota nebude v XML zapsána. Budeme potřebovat někam ukládat jméno aktuálního elementu, k tomu si zadefinujeme stringovou proměnnou element. Kód píšeme pochopitelně do using bloku.

string jmeno = "";
int vek = 0;
DateTime registrovan = DateTime.Now;
string element = "";

Začneme načítat soubor. XmlReader načítá soubor řádek po řádku, odshora dolů. Na jeho instanci voláme metodu Read(). Ta nám při každém zavolání načte další tzv. uzel. Uzlem může být element, případně atribut nebo textová hodnota elementu (nás bude zajímat Element, Text a EndElement), dalším typem uzlu může být např. komentář, které pro nás nyní nebudou důležité. Pokud je čtečka na konci souboru, vrátí metoda Read() false, v opačném případě vrací true. Postoupíme tedy k postupnému načítání všech uzlů v dokumentu pomocí while cyklu:

while (xr.Read())
{
}

Na instanci XmlReaderu máme několik užitečných vlastností, budeme používat NodeType, ve které je uložen typ aktuálního uzlu, na kterém se čtečka nachází. Dále použijeme vlastnosti Name a Value, ve kterých je uloženo jméno aktuálního uzlu a jeho hodnota (pokud nějakou má).

Nás budou nyní zajímat 2 typy uzlů, Element a Text. Pojďme na ně reagovat, zatím napíšeme podmínky s prázdnými těly:

// načítáme element
if (xr.NodeType == XmlNodeType.Element)
{
}
// načítáme hodnotu elementu
else if (xr.NodeType == XmlNodeType.Text)
{
}

Nyní vložíme kód do první podmínky. Budeme tedy reagovat na načtení elementu. Je zde potřeba provést 2 akce.

Klíčovou akcí bude uložení názvu elementu do proměnné element. Tak budeme vzápětí schopni v druhé podmínce zjistit, kterého elementu je text, který právě čteme.

Pokaždé, když narazíme na element uzivatel, načteme atribut věk pomocí metody GetAttribute(), jejímž parametrem je název atributu. Atribut aktuálního elementu lze načíst takto jednoduše. S hodnotou to tak jednoduché není, sice existují metody ReadContentAsTyp(), ale pozor, ty z nevysvětlitelného důvodu provedou Read() a tak rozbijí běh while cyklu. V nezanořených XML by čtečka nefungovala správně. Nějakou dobu jsem se snažil problém vyřešit, ale řešení byla natolik krkolomná, že jsem došel k závěru ReadContentAs...() vůbec nepoužívat. Obsah první podmínky tedy bude vypadat takto:

element = xr.Name; // název aktuálního elementu
if (element == "uzivatel")
{
        vek = int.Parse(xr.GetAttribute("vek"));
}

Přejděme do další větve, tedy do zpracování hodnoty elementu. Zde využijeme předem uložené hodnoty názvu elementu, kterou si vložíme do switche. Podle elementu uložíme hodnotu do dané vlastnosti uživatele:

switch (element)
{
        case "jmeno":
                jmeno = xr.Value;
                break;
        case "registrovan":
                registrovan = DateTime.Parse(xr.Value);
                break;
}

Již jsme velmi blízko, bystřejší si jistě všimli, že uživatele nikde nepřidáváme. Jeho přidání nastane ve chvíli načtení uzavíracího elementu uzivatel. K našim dvoum podmínkám tedy přidáme třetí:

// načítáme ukončující element
else if ((xr.NodeType == XmlNodeType.EndElement) && (xr.Name == "uzivatel"))
        uzivatele.Add(new Uzivatel(jmeno, vek, registrovan));

Máme hotovo :)

Pro jistotu přikládám kompletní kód načtení souboru:

using (XmlReader xr = XmlReader.Create(@"soubor.xml"))
{
        string jmeno = "";
        int vek = 0;
        DateTime registrovan = DateTime.Now;
        string element = "";
        while (xr.Read())
        {
                // načítáme element
                if (xr.NodeType == XmlNodeType.Element)
                {
                        element = xr.Name; // název aktuálního elementu
                        if (element == "uzivatel")
                        {
                                vek = int.Parse(xr.GetAttribute("vek"));
                        }
                }
                // načítáme hodnotu elementu
                else if (xr.NodeType == XmlNodeType.Text)
                {
                        switch (element)
                        {
                                case "jmeno":
                                        jmeno = xr.Value;
                                        break;
                                case "registrovan":
                                        registrovan = DateTime.Parse(xr.Value);
                                        break;
                        }
                }
                // načítáme ukončující element
                else if ((xr.NodeType == XmlNodeType.EndElement) && (xr.Name == "uzivatel"))
                        uzivatele.Add(new Uzivatel(jmeno, vek, registrovan));
        }
}

Zbývá uživatele vypsat, abychom věděli, že jsme je načetli správně. Upravíme si metodu ToString() ve třídě Uzivatel tak, aby vracela všechny hodnoty:

public override string ToString()
{
        return String.Format("{0}, {1}, {2}", Jmeno, Vek, Registrovan.ToShortDateString());
}

Uživatele jednoduše vypíšeme:

// výpis načtených objektů
foreach (Uzivatel u in uzivatele)
{
        Console.WriteLine(u);
}
Console.ReadKey();

a výsledek:

Výsledek načtení objektů z XML SAXem v C# .NET

Pokud se vám načítání příliš nelíbilo, dám vám za pravdu. Zatímco generování nového XML souboru je SAXem velmi jednoduché a přirozené, načítání je opravdu krkolomné. V příští lekci, Práce s XML soubory pomocí DOM v C#, se podíváme na DOM, tedy objektový přístup k XML dokumentu.


 

Stáhnout

Staženo 533x (28.02 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
5 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Miniatura
Předchozí článek
Úvod do XML a zápis SAXem
Miniatura
Všechny články v sekci
Práce se soubory v C#
Miniatura
Následující článek
Práce s XML soubory pomocí DOM v C#
Aktivity (3)

 

 

Komentáře
Zobrazit starší komentáře (2)

Avatar
Jan Vargovský
Redaktor
Avatar
Jan Vargovský:6.6.2014 17:48

Zamysli se nad tou chybou, pak to třeba dojde.

 
Odpovědět 6.6.2014 17:48
Avatar
Yahkem
Redaktor
Avatar
Yahkem:7.6.2014 15:57

Dik za super radu, uz to funguje

 
Odpovědět 7.6.2014 15:57
Avatar
Petr Stastny
Redaktor
Avatar
Petr Stastny:12.5.2016 11:34

Ahoj, potrebuju pomoct. Mam XML soubor

<?xml version="1.0" encoding="utf-8"?>
<Rise Datum="06:20:50-05.05.2016">
  <Uzivatel>EAAAAJnv4qf4VJqGw1or6jR55lpVmwjzte5JVVSvUuXY1JEY</Uzivatel>
  <Rise_Jmeno>EAAAAD5gVojQLRQY2oFKfsntO4IxO+UtxHKNSJJdP4/8nSSj</Rise_Jmeno>
  <Rise_Penize>EAAAANU3j2ivL4mqVa18GZAI+S/Mg71pOQKbh7arG+98JJsf</Rise_Penize>
  <Rise_Demokr>EAAAAHzSmASCOqlOTt8YgjTXhCKWN/GTLiNhSwBTxXDKfn7S</Rise_Demokr>
  <Rise_Uzemi>EAAAAOgnw+l+AzhVXniu8DNYUxka2IejIKmk9+77OBWJBABF</Rise_Uzemi>
  <Obyv_Chudina>EAAAAPTKqQvLYg8/QE3SjwhF2AqCs1DYHiJtpZv35Bge5u7t</Obyv_Chudina>
  <Vern_Chudina>EAAAAJ4vKnalsv04NFVWibbPFmswHdJcaJLsWosPamLyQRRl</Vern_Chudina>
  <Obyv_Mestane>EAAAAAPV62xYqXEQh/zwsC2zMlPnrmiZbBDSf0ssiYf9PgLs</Obyv_Mestane>
  <Vern_Mestane>EAAAABu8qAsN63413I7ScTjJU8kA6UsGHoydBczwUlAMmc0y</Vern_Mestane>
  <Obyv_Intelig>EAAAAKn6zCuex4DZQVNfjSsmTv2eRANKcEdnEla2NCny/ARy</Obyv_Intelig>
  <Vern_Intelig>EAAAABZhhQ5cN87bqboZHQ8thzmiKqRa2ZORoZe3JPU2AQ2o</Vern_Intelig>
  <Pocet_Armada>EAAAAO+eNvDteclkeuVXlvF54dbeANu2hVkHFJvB3vtG9VjY</Pocet_Armada>
  <Vern_Armada>EAAAAFkLu1o8Fm29xke6xcAdFd4b4fxYeIT9zT2pahtBSEOx</Vern_Armada>
  <Pocet_Tpol>EAAAAIfudicHc+PhwGRW5UrZwKtHX3UHSZ3QH6LR0RwRWi3Q</Pocet_Tpol>
  <Vern_Tpol>EAAAADb3ah6ZklTAA/QX6ze/eSn2gUvCRFv3Mpqt38ORhC57</Vern_Tpol>
  <Pocet_BodyG>EAAAAFzce9RoqdbluxSkDz7TlGQpSloWWtjamrTmE7qIq4QH</Pocet_BodyG>
  <Vern_BodyG>EAAAABraj7d9Q+C4CoERgc+JuyIfpNeQIqOvDzgMng+qVse5</Vern_BodyG>
</Rise>

Jak nactu treba Uzivatel nebo Rise_Jmeno? Tenhle kod nacte jenom datum rise:

using (XmlReader xr = XmlReader.Create(cesta + soubor))
            {
                while (xr.Read()) {
                    if(xr.NodeType == XmlNodeType.Element)
                    {
                        if (xr.Name == "Rise")  // Jestlize to je rise nacti datum a uloz ho do promenny
                        {
                            Rise_Datum = (xr.GetAttribute("Datum"));
                        }
                    }else if(xr.NodeType == XmlNodeType.Text)
                    {
                        switch (xr.Name)
                        {
                            case "Uzivatel":
                                Uzivatel = Crypto.DecryptStringAES(xr.Value, Klic.Remove(5, 2));
                                break;
                                ...
 
Odpovědět 12.5.2016 11:34
Avatar
Garrom Orc Shaman:26.9.2016 15:43

na jakou verzi .net framevorku to funguje ?
zkouším verzi 4.0 a dostávám z tohoto kódu

public static void Main(string[] args)
{
        using (XmlReader xr = XmlReader.Create(@"soubor.xml"))
        {

        }
}

tyto chyby :

  • V oboru názvů XmlReader neexistuje název typu nebo oboru názvů Create (pravděpodobně jste neuvedli odkaz na sestavení). (CS0234)
  • XmlReader je obor názvů, ale používá se jako typ.
Odpovědět 26.9.2016 15:43
We're orcs, maybe we are not always wise or beautiful, but we will always be strong, outnumbered and well armed
Avatar
UzivatelXYZ
Člen
Avatar
UzivatelXYZ:25. června 20:09

Dobrý deň, asi som debil, ale mohol by mi niekto povedať, čo je na tomto zle? ID sa načítava správne, ale meno ani dátum registrácie nie (C# z nejakého dôvodu číta stále prázdne stringy). Skúšal som všetko možné, pridať aj odobrať z XML úvodzovky, brať každý Node ako Element aj rozlišovať Node a Text, ale nič nepomáha.

Zdroják:

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            List<User> users = new List<User>();
            int? id = null;
            string name = null;
            DateTime? registered = null;

            using (XmlReader xr = XmlReader.Create(@"d:/plocha/Python/users.xml"))
            {
                while (xr.Read())
                {
                    if (xr.NodeType == XmlNodeType.Element)
                    {
                        switch (xr.Name)
                        {
                            case "user":
                                id = int.Parse(xr.GetAttribute("id"));
                                break;
                            case "name":
                                name = xr.Value;
                                break;
                            case "registered":
                                try
                                { registered = DateTime.Parse(xr.Value); }
                                catch { }
                                break;
                        }
                    }

                    if (id.HasValue && name != null && registered.HasValue)
                    {
                        users.Add(new User((int)id, name, (DateTime)registered));
                        id = null;
                        name = null;
                        registered = null;
                    }
                }
            }

            foreach (User u in users) { Console.WriteLine(u.ToString()); }
            Console.WriteLine(users.Count);
            Console.ReadKey();
        }
    }

    public class User
    {
        public int id { get; protected set; }
        public string name {get; protected set; }
        public DateTime registered { get; protected set; }

        public User(int id, string name, DateTime registered)
        {
            this.id = id;
            this.name = name;
            this.registered = registered;
        }

        public override string ToString()
        {
            return String.Format("{0} {1} {2}", id, name, registered);
        }
    }
}

XML:

<?xml version="1.0" encoding="utf-8"?>
<users>
  <user id="1">
    <name>"Pavel Slavik"</name>
    <registered>"21.3.2000"</registered>
  </user>
  <user id="2">
    <name>"Jan Novak"</name>
    <registered>"30.10.2012"</registered>
  </user>
  <user id="3">
    <name>"Tomas Marny"</name>
    <registered>"12.1.2011"</registered>
  </user>
</users>

Za pomoc vopred ďakujem.

 
Odpovědět 25. června 20:09
Avatar
FoByCZ
Člen
Avatar
Odpovídá na UzivatelXYZ
FoByCZ:31. července 4:47

Protože máš podmínku jen na XmlNodeType.E­lement. Ty ostatní věci musíš načítat z XmlNodeType.Text. Projdi si ještě jednou tento díl tutoriálu ;)

Editováno 31. července 4:49
 
Odpovědět  +1 31. července 4:47
Avatar
FoByCZ
Člen
Avatar
Odpovídá na Garrom Orc Shaman
FoByCZ:31. července 4:48

Možná Ti chybí:

using System.Xml;
 
Odpovědět 31. července 4:48
Avatar
UzivatelXYZ
Člen
Avatar
Odpovídá na FoByCZ
UzivatelXYZ:31. července 12:35

Pardon, som proste dilino :) Nevšimol som si, že obsah vnorených elementov je typu Text. A to aj keď ma mohla kopnúť múza už pri traceovaní, kde sú údaje o každom node :D Každopádne ďakujem za pomoc.

 
Odpovědět 31. července 12:35
Avatar
Odpovídá na FoByCZ
Garrom Orc Shaman:3. srpna 22:23

Ano, chybělo. Ovšem to je velice dlouho a za tu dobu jsem udělal veliké pokroky. Momentálně se k klasickému C# doučuji Unity3D api a pracuji na remaku hry zvané Battle City...
Jinak děkuji za odpověď, už je to trochu dlouho ale chtěl jsi mi pomoct a já si toho cením (nehledě na velké zpoždění) :-)

Odpovědět 3. srpna 22:23
We're orcs, maybe we are not always wise or beautiful, but we will always be strong, outnumbered and well armed
Avatar
FoByCZ
Člen
Avatar
Odpovídá na Garrom Orc Shaman
FoByCZ:4. srpna 1:56

Jsem rád, že jsi sám tak pokročil. I když jsem tušil, že jsi se určitě nezasekl na tomhle místě a nečekal 3/4 roku na odpověď :)
Ale přišlo mi správné, aby tady ta odpověď byla, aby si ji mohl přečís i nový návštěvník jako třeba já, který teprve začínám ;)

 
Odpovědět 4. srpna 1:56
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 12. Zobrazit vše