Underviser i øjeblikket en stor flok konsulenter på Teknologisk Institut som skal igennem en del stof for at blive klar til en af de populære MCTS certificeringer. En af de ting vi arbejdede med i dag var nogle af de klasser der er til rådighed i frameworket til at arbejde med XML - herunder en af de sværeste at have med at gøre - XmlTextReader (som arver fra XmlReader). Jeg var ikke helt forberedt på at vi kiggede så konkret på den klasse, og var derfor ikke tilstrækkeligt klædt på. Sidder nu i toget på vej hjem, og syntes jeg burde bruge lidt tid på at dække hullerne med et ordentlig eksempel.
Brug af XmlTextReader-klassen svarer til at "lægge træet ned" og arbejde med elementerne et af gangen på en forward only og read only måde. Det er dog ikke hele strukturen der blive fladet ud - eksempelvis skal attributter håndteres specielt.
Den nemmeste måde at arbejde med XmlTextReader-klassen er at benytte Read() metoden som blot læser et element af gangen, men man skal være opmærksom på at element kan være mange ting - eksempelvis en deklaration, kommentar, whitespace og ligende - og det gør det hele lidt mere kompliceret (men logisk nok når man lige lære at tænke rigtigt).
Se på denne simple XML struktur
<?xml version="1.0" encoding="utf-8" ?>
<medarbejdere>
<!-- Kommentar før medarbejder -->
<medarbejder>
<fornavn id="1" erLeder="false">Michell</fornavn>
<efternavn>Cronberg</efternavn>
<beskrivelse>
<![CDATA[<p>Bla Bla Bla</p>]]>
</beskrivelse>
</medarbejder>
<medarbejder>
<!-- Kommentar før 2. medarbejders fornavn -->
<fornavn id="2">Mathias</fornavn>
<efternavn>Cronberg</efternavn>
</medarbejder>
</medarbejdere>
Den benytte mange af de almindelige elementer i en XML-stuktur - herunder deklaration, kommentarer, elementer, attributter, CDATA, og whitespaces. Lad os se på hvordan den kan løbes igennem med en XmlTextReader med brug af Read(). For at gøre det nemt at læse er der lavet disse tre hjælpemetoder:
static void wl(string key)
{
System.Console.WriteLine(key.PadRight(25));
}
static void wl(string key, string value)
{
System.Console.WriteLine(key.PadRight(25) + ": " + value);
}
static void wlr(XmlTextReader r)
{
System.Console.Write(r.LineNumber.ToString("000") + "|" + (r.LinePosition).ToString("000") + " ");
}
De to metoder (wl()) skriver blot ud på Console, og wlr() skriver på basis af et XmlTextReader-objekt både linjenummer og linjeposition ud.
Her er selve koden der looper hele strukturen igennem:
XmlTextReader r = new XmlTextReader("test.xml");
while (r.Read())
{
wlr(r);
switch (r.NodeType)
{
case XmlNodeType.Element:
wl("Start element " +"Att=" + r.AttributeCount.ToString(), r.Name);
if(r.AttributeCount>0)
for (int i = 0; i < r.AttributeCount; i++)
{
r.MoveToAttribute(i);
wlr(r);
wl("Attribut="+r.Name, r.Value);
}
break;
case XmlNodeType.EndElement:
wl("End element", r.Name);
break;
case XmlNodeType.Text:
wl("Text",r.Value);
break;
case XmlNodeType.Comment:
wl("Comment", r.Value.Trim());
break;
case XmlNodeType.Whitespace:
wl("Whitespace");
break;
case XmlNodeType.CDATA:
wl("CData", r.Value.Trim());
break;
case XmlNodeType.XmlDeclaration:
wl("XmlDeclaration");
break;
default:
throw new ApplicationException("Ukendt XmlNodeType");
}
}
r.Close();
Læg mærke til at der switch'es på alle de i strukturen kendte typer. Resultatet er som følger:
001|003 XmlDeclaration
001|040 Whitespace
002|002 Start element Att=0 : medarbejdere
002|015 Whitespace
003|007 Comment : Kommentar for medarbejder
003|037 Whitespace
004|004 Start element Att=0 : medarbejder
004|016 Whitespace
005|006 Start element Att=2 : fornavn
005|014 Attribut=id : 1
005|021 Attribut=erLeder : false
005|037 Text : Michell
005|046 End element : fornavn
005|054 Whitespace
006|006 Start element Att=0 : efternavn
006|016 Text : Cronberg
006|026 End element : efternavn
006|036 Whitespace
007|006 Start element Att=0 : beskrivelse
007|018 Whitespace
008|016 CData : <p>Bla Bla Bla</p>
008|058 Whitespace
009|007 End element : beskrivelse
009|019 Whitespace
010|005 End element : medarbejder
010|017 Whitespace
011|004 Start element Att=0 : medarbejder
011|016 Whitespace
012|009 Comment : Kommentar for 2. medarbejders fornavn
012|051 Whitespace
013|006 Start element Att=1 : fornavn
013|014 Attribut=id : 2
013|021 Text : Mathias
013|030 End element : fornavn
013|038 Whitespace
014|006 Start element Att=0 : efternavn
014|016 Text : Cronberg
014|026 End element : efternavn
014|036 Whitespace
015|005 End element : medarbejder
015|017 Whitespace
016|003 End element : medarbejdere
Læg mærke til hvordan deklaration, whitespaces og kommentarer behandles, og at attributter håndteres helt separat ved hjælpe af MoveToAttribute(). Hvis du leger lidt videre med koden selv så prøv at trække hele XML-strukturen sammen på en linje (fjerner whitespaces) og fjerne deklaration og kommentarer. Det gør XML-filen uoverskuelig men resultatet af koden bliver mere renere.
Med basis i ovennævnte er det nemmere at prøve nogle at de mere brugbare metoder som MoveToContent(), ReadStartElement() og ReadElementString() der gør gennemløb af strukturen noget nemmere end blot brug af Read(). Det ser vi på i et senere indlæg (for nu kører vi ind på Odense banegård ;)