<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>Træer</title><link>http://activedeveloper.dk/weblogs/mrjs/category/57.aspx</link><description>Indhold der relaterer til træstrukturer og anden form for hirakisk data representation.</description><managingEditor>Jesper Jensen</managingEditor><dc:language>da-DK</dc:language><generator>.Text Version 0.95.2004.102</generator><item><dc:creator>Jesper Jensen</dc:creator><title>Træer V - Reloaded</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2006/03/09/2536.aspx</link><pubDate>Thu, 09 Mar 2006 15:23:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2006/03/09/2536.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/2536.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2006/03/09/2536.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/2536.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/2536.aspx</trackback:ping><description>&lt;p&gt;Jeg skrev for et stykke tid siden en række poster med den fælles overskrift &lt;em&gt;Træer&lt;/em&gt; (&lt;a title="Post oversigt" href="/weblogs/mrjs/category/57.aspx"&gt;Se her&lt;/a&gt;). I een af disse poste, lavede jeg et eksempel, hvor jeg brugte de nye hierakiske kontroller i ASP.NET til at vise en hierakisk Xml-struktur. I den forbindelse er der flere der har efterspurgt en forklaring/forslag til hvordan man kan danne en Xml-repræsentation af en filstruktur. Altså hvor rodelementet er en vilkårlig mappe, og underelementerne repræsenterer filer og mapper. Xml strukturen kan f.eks bruges til en &lt;em&gt;Explorer&lt;/em&gt; agtig webside med oversigt over filer på en host.&lt;/p&gt;
&lt;p&gt;Opgven er faktisk lige til, da vi jo har en &lt;em&gt;XmlWriter&lt;/em&gt; klasse, som kan bruges til at generere det hierakiske output, og som sikre at Xml'en bliver formateret/struktureret korekt. Man kan ikke instatntierer en &lt;em&gt;XmlWriter&lt;/em&gt; med en konstruktør (new), da det er en abstrakt klasse. I .NET 2.0 kan man dog danne en instans af &lt;em&gt;XmlWriter&lt;/em&gt; klasse via den statiske metode &lt;em&gt;Create&lt;/em&gt;, som har 10 overloads der tage forskellige typer. Her i blandt også en streng, som repræsenterer et filnavn - i nedenstående eksempel, har jeg dog brugt den overload der tage en &lt;em&gt;Stream.&lt;/em&gt; Så er det så dejligt nemt at se hvad der sker i en konsol-applikation.&lt;/p&gt;
&lt;p&gt;&lt;img id="114191765063.JPG" title="Filstruktur.JPG" alt="Filstruktur.JPG" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_Filstruktur.JPG" border="0" /&gt;&lt;/p&gt;
&lt;p&gt;Når &lt;em&gt;XmlWriteren&lt;/em&gt; er instantieret, er det blot at starte fra toppen af strukturen, og danne sit rodelement (her &lt;em&gt;root&lt;/em&gt;), og så bare forsætte i en rekursiv metode, ind til alle underelementer er blevet renderet.&lt;/p&gt;
&lt;p&gt;Læg mærke til at den rekursive metode tager både et &lt;em&gt;DirectoryInfo-&lt;/em&gt; og et &lt;em&gt;XmlWriter&lt;/em&gt;objekt. På den måde indlejres alle filer og undermapper fint i hinnanden...&lt;/p&gt;&lt;pre style="COLOR: #000000"&gt;&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;1&lt;/span&gt;&lt;span style="COLOR: #0000ff"&gt;using&lt;/span&gt; System.IO;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;2&lt;/span&gt;&lt;span style="COLOR: #0000ff"&gt;using&lt;/span&gt; System.Text;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;3&lt;/span&gt;&lt;span style="COLOR: #0000ff"&gt;using&lt;/span&gt; System.Xml;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;4&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;5&lt;/span&gt;&lt;span style="COLOR: #0000ff"&gt;namespace&lt;/span&gt; ConsoleApplication1
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;6&lt;/span&gt;{
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;7&lt;/span&gt; &lt;span style="COLOR: #0000ff"&gt;class&lt;/span&gt; Program
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;8&lt;/span&gt; {
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;9&lt;/span&gt; &lt;span style="COLOR: #0000ff"&gt;static&lt;/span&gt; &lt;span style="COLOR: #0000ff"&gt;void&lt;/span&gt; Main(&lt;span style="COLOR: #0000ff"&gt;string&lt;/span&gt;[] args)
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;10&lt;/span&gt; {
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;11&lt;/span&gt;&lt;span style="COLOR: #6699cc"&gt;    #region XmlSettings&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;12&lt;/span&gt;    XmlWriterSettings settings = &lt;span style="COLOR: #0000ff"&gt;new&lt;/span&gt; XmlWriterSettings();
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;13&lt;/span&gt;    settings.Indent = &lt;span style="COLOR: #0000ff"&gt;true&lt;/span&gt;;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;14&lt;/span&gt;    settings.NewLineHandling = NewLineHandling.Entitize;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;15&lt;/span&gt;    settings.NewLineOnAttributes = &lt;span style="COLOR: #0000ff"&gt;true&lt;/span&gt;;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;16&lt;/span&gt;    settings.Encoding = Encoding.UTF8;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;17&lt;/span&gt;&lt;span style="COLOR: #6699cc"&gt;    #endregion&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;18&lt;/span&gt;    DirectoryInfo dir = Directory.GetParent(&lt;span style="COLOR: #848284"&gt;"../../"&lt;/span&gt;);
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;19&lt;/span&gt;    XmlWriter w = XmlWriter.Create(Console.Out, settings);
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;20&lt;/span&gt; 
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;21&lt;/span&gt;    w.WriteStartDocument();
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;22&lt;/span&gt;    w.WriteStartElement(&lt;span style="COLOR: #848284"&gt;"root"&lt;/span&gt;);
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;23&lt;/span&gt;    w.WriteAttributeString(&lt;span style="COLOR: #848284"&gt;"name"&lt;/span&gt;, Path.GetFileName(dir.FullName));
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;24&lt;/span&gt;       WriteSubElements(dir, w);
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;25&lt;/span&gt;    w.WriteEndElement();&lt;span style="COLOR: #008200"&gt;// root&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;26&lt;/span&gt;    w.Close();
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;27&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;28&lt;/span&gt;    Console.Read();
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;29&lt;/span&gt; }
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;30&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;31&lt;/span&gt; &lt;span style="COLOR: #0000ff"&gt;private&lt;/span&gt; &lt;span style="COLOR: #0000ff"&gt;static&lt;/span&gt; &lt;span style="COLOR: #0000ff"&gt;void&lt;/span&gt; WriteSubElements(DirectoryInfo dirInfo, XmlWriter w)
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;32&lt;/span&gt; {
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;33&lt;/span&gt;    &lt;span style="COLOR: #008200"&gt;// Først alle undermapper&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;34&lt;/span&gt;    &lt;span style="COLOR: #0000ff"&gt;foreach&lt;/span&gt; (DirectoryInfo dir &lt;span style="COLOR: #0000ff"&gt;in&lt;/span&gt; dirInfo.GetDirectories())
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;35&lt;/span&gt;    {
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;36&lt;/span&gt;       w.WriteStartElement(&lt;span style="COLOR: #848284"&gt;"dir"&lt;/span&gt;);
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;37&lt;/span&gt;       w.WriteAttributeString(&lt;span style="COLOR: #848284"&gt;"name"&lt;/span&gt;, Path.GetFileName(dir.FullName));
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;38&lt;/span&gt;       w.WriteAttributeString(&lt;span style="COLOR: #848284"&gt;"changed"&lt;/span&gt;, dir.LastWriteTime.ToString(&lt;span style="COLOR: #848284"&gt;"s"&lt;/span&gt;));
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;39&lt;/span&gt;       &lt;span style="COLOR: #008200"&gt;// Og rekursivt...&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;40&lt;/span&gt;       WriteSubElements(dir, w);
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;41&lt;/span&gt;       w.WriteEndElement(); &lt;span style="COLOR: #008200"&gt;//dir&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;42&lt;/span&gt;    }
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;43&lt;/span&gt;    &lt;span style="COLOR: #008200"&gt;// Så alle filer&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;44&lt;/span&gt;    &lt;span style="COLOR: #0000ff"&gt;foreach&lt;/span&gt; (FileInfo fi &lt;span style="COLOR: #0000ff"&gt;in&lt;/span&gt; dirInfo.GetFiles())
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;45&lt;/span&gt;    {
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;46&lt;/span&gt;       w.WriteStartElement(&lt;span style="COLOR: #848284"&gt;"file"&lt;/span&gt;);
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;47&lt;/span&gt;       w.WriteAttributeString(&lt;span style="COLOR: #848284"&gt;"name"&lt;/span&gt;, Path.GetFileName(fi.FullName));
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;48&lt;/span&gt;       w.WriteAttributeString(&lt;span style="COLOR: #848284"&gt;"changed"&lt;/span&gt;, fi.CreationTime.ToString(&lt;span style="COLOR: #848284"&gt;"s"&lt;/span&gt;));
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;49&lt;/span&gt;       w.WriteEndElement();&lt;span style="COLOR: #008200"&gt;//file&lt;/span&gt;
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;50&lt;/span&gt;    }
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;51&lt;/span&gt; }
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;52&lt;/span&gt; }
&lt;span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right"&gt;53&lt;/span&gt;}&lt;/pre&gt;
&lt;p&gt;Som det fremgår, så er det ret simpelt, når blot man lige holder styr på sine &lt;em&gt;start&lt;/em&gt;- og &lt;em&gt;end-&lt;/em&gt;elementer.&lt;/p&gt;
&lt;p&gt;Code on...&lt;/p&gt;
&lt;p id="zoundry_bw_tags"&gt;&lt;!-- Tag links generated by Zoundry Blog Writer. Do not manually edit. http://www.zoundry.com --&gt;&lt;span class="tags"&gt;&lt;span class="tagspaces"&gt;Technorati&lt;/span&gt; : &lt;a href="http://technorati.com/tag/Xml" rel="tag"&gt;Xml&lt;/a&gt;&lt;/span&gt; &lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/2536.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Jeg skrev for et stykke tid siden en række poster med den fælles overskrift <em>Træer</em> (<a title="Post oversigt" href="/weblogs/mrjs/category/57.aspx">Se her</a>). I een af disse poste, lavede jeg et eksempel, hvor jeg brugte de nye hierakiske kontroller i ASP.NET til at vise en hierakisk Xml-struktur. I den forbindelse er der flere der har efterspurgt en forklaring/forslag til hvordan man kan danne en Xml-repræsentation af en filstruktur. Altså hvor rodelementet er en vilkårlig mappe, og underelementerne repræsenterer filer og mapper. Xml strukturen kan f.eks bruges til en <em>Explorer</em> agtig webside med oversigt over filer på en host.</p>
<p>Opgven er faktisk lige til, da vi jo har en <em>XmlWriter</em> klasse, som kan bruges til at generere det hierakiske output, og som sikre at Xml'en bliver formateret/struktureret korekt. Man kan ikke instatntierer en <em>XmlWriter</em> med en konstruktør (new), da det er en abstrakt klasse. I .NET 2.0 kan man dog danne en instans af <em>XmlWriter</em> klasse via den statiske metode <em>Create</em>, som har 10 overloads der tage forskellige typer. Her i blandt også en streng, som repræsenterer et filnavn - i nedenstående eksempel, har jeg dog brugt den overload der tage en <em>Stream.</em> Så er det så dejligt nemt at se hvad der sker i en konsol-applikation.</p>
<p><img id="114191765063.JPG" title="Filstruktur.JPG" alt="Filstruktur.JPG" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_Filstruktur.JPG" border="0" /></p>
<p>Når <em>XmlWriteren</em> er instantieret, er det blot at starte fra toppen af strukturen, og danne sit rodelement (her <em>root</em>), og så bare forsætte i en rekursiv metode, ind til alle underelementer er blevet renderet.</p>
<p>Læg mærke til at den rekursive metode tager både et <em>DirectoryInfo-</em> og et <em>XmlWriter</em>objekt. På den måde indlejres alle filer og undermapper fint i hinnanden...</p><pre style="COLOR: #000000"><span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">1</span><span style="COLOR: #0000ff">using</span> System.IO;
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">2</span><span style="COLOR: #0000ff">using</span> System.Text;
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">3</span><span style="COLOR: #0000ff">using</span> System.Xml;
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">4</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">5</span><span style="COLOR: #0000ff">namespace</span> ConsoleApplication1
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">6</span>{
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">7</span> <span style="COLOR: #0000ff">class</span> Program
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">8</span> {
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">9</span> <span style="COLOR: #0000ff">static</span> <span style="COLOR: #0000ff">void</span> Main(<span style="COLOR: #0000ff">string</span>[] args)
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">10</span> {
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">11</span><span style="COLOR: #6699cc">    #region XmlSettings</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">12</span>    XmlWriterSettings settings = <span style="COLOR: #0000ff">new</span> XmlWriterSettings();
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">13</span>    settings.Indent = <span style="COLOR: #0000ff">true</span>;
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">14</span>    settings.NewLineHandling = NewLineHandling.Entitize;
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">15</span>    settings.NewLineOnAttributes = <span style="COLOR: #0000ff">true</span>;
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">16</span>    settings.Encoding = Encoding.UTF8;
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">17</span><span style="COLOR: #6699cc">    #endregion</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">18</span>    DirectoryInfo dir = Directory.GetParent(<span style="COLOR: #848284">"../../"</span>);
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">19</span>    XmlWriter w = XmlWriter.Create(Console.Out, settings);
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">20</span> 
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">21</span>    w.WriteStartDocument();
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">22</span>    w.WriteStartElement(<span style="COLOR: #848284">"root"</span>);
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">23</span>    w.WriteAttributeString(<span style="COLOR: #848284">"name"</span>, Path.GetFileName(dir.FullName));
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">24</span>       WriteSubElements(dir, w);
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">25</span>    w.WriteEndElement();<span style="COLOR: #008200">// root</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">26</span>    w.Close();
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">27</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">28</span>    Console.Read();
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">29</span> }
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">30</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">31</span> <span style="COLOR: #0000ff">private</span> <span style="COLOR: #0000ff">static</span> <span style="COLOR: #0000ff">void</span> WriteSubElements(DirectoryInfo dirInfo, XmlWriter w)
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">32</span> {
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">33</span>    <span style="COLOR: #008200">// Først alle undermapper</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">34</span>    <span style="COLOR: #0000ff">foreach</span> (DirectoryInfo dir <span style="COLOR: #0000ff">in</span> dirInfo.GetDirectories())
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">35</span>    {
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">36</span>       w.WriteStartElement(<span style="COLOR: #848284">"dir"</span>);
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">37</span>       w.WriteAttributeString(<span style="COLOR: #848284">"name"</span>, Path.GetFileName(dir.FullName));
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">38</span>       w.WriteAttributeString(<span style="COLOR: #848284">"changed"</span>, dir.LastWriteTime.ToString(<span style="COLOR: #848284">"s"</span>));
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">39</span>       <span style="COLOR: #008200">// Og rekursivt...</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">40</span>       WriteSubElements(dir, w);
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">41</span>       w.WriteEndElement(); <span style="COLOR: #008200">//dir</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">42</span>    }
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">43</span>    <span style="COLOR: #008200">// Så alle filer</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">44</span>    <span style="COLOR: #0000ff">foreach</span> (FileInfo fi <span style="COLOR: #0000ff">in</span> dirInfo.GetFiles())
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">45</span>    {
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">46</span>       w.WriteStartElement(<span style="COLOR: #848284">"file"</span>);
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">47</span>       w.WriteAttributeString(<span style="COLOR: #848284">"name"</span>, Path.GetFileName(fi.FullName));
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">48</span>       w.WriteAttributeString(<span style="COLOR: #848284">"changed"</span>, fi.CreationTime.ToString(<span style="COLOR: #848284">"s"</span>));
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">49</span>       w.WriteEndElement();<span style="COLOR: #008200">//file</span>
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">50</span>    }
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">51</span> }
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">52</span> }
<span style="BORDER-RIGHT: #999999 1px solid; WIDTH: 40px; COLOR: #008284; MARGIN-RIGHT: 10px; BACKGROUND-COLOR: #e5e5e5; TEXT-ALIGN: right">53</span>}</pre>
<p>Som det fremgår, så er det ret simpelt, når blot man lige holder styr på sine <em>start</em>- og <em>end-</em>elementer.</p>
<p>Code on...</p>
<p id="zoundry_bw_tags"><!-- Tag links generated by Zoundry Blog Writer. Do not manually edit. http://www.zoundry.com --><span class="tags"><span class="tagspaces">Technorati</span> : <a href="http://technorati.com/tag/Xml" rel="tag">Xml</a></span> </p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/2536.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Træer IV</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/04/23/TreesIV.aspx</link><pubDate>Sat, 23 Apr 2005 19:53:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/04/23/TreesIV.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/495.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/04/23/TreesIV.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/495.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/495.aspx</trackback:ping><description>&lt;p&gt;Her kommer den foreløbigt sidste post i min lille serie om træstrukturer (&lt;a href="http://activedeveloper.dk/weblogs/mrjs/category/57.aspx"&gt;index&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Nu er vi bevæbnet til tænderne med metoder til generering af træstrukturer i XML, og vi har netop downloadet beta 2 af VS.NET 2005. Skulle vi så ikke tage et kik på et par af de nye kontroller, som er blevet introduceret til os med ASP.NET 2.0, og se om ikke kan fremstille nogle flotte træstrukturer.&lt;/p&gt;
&lt;p&gt;Start med at lave et nyt websted (eller forsæt på et eksisterende), og tilføj en ny webform. På formen slæber du en af de nye datakontroller ind: &lt;i&gt;XmlDataSource&lt;/i&gt;-kontrollen. Den har en række egenskaber, men vi vil nøjes med at bruge den der hedder &lt;i&gt;DataFile&lt;/i&gt;. Den angiver stien til Xml-filen der indeholder vores træstruktur fra tidligere (i mit web bliver det: "~/Trees/TreeData.xml"). Så slæber vi en webkontrol mere ind på siden: &lt;i&gt;TreeView&lt;/i&gt;. I designeren fremkommer der et lille mærke (smarttag), som man kan aktiverer, og få adgang til en række vigtige vizards på den enkelte kontrol.&lt;/p&gt;&lt;img alt="Smarttag i VS.NET 2005" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIV_1.gif" /&gt; 
&lt;p&gt;På TreeView er der flere punkter, men den vigtigste er angivelsen af datakilden, som er repræsenteret ved punktet &lt;i&gt;Choose Data Source&lt;/i&gt;. Ud for punktet er der en dropdownliste, som bl.a. indeholder vores XmlDataSource - den vælges. Her efter går vi videre til at skulle redigerer bindingen af vores datakilde til TreeView-delene. Det gøres via Smarttag-punktet &lt;i&gt;Edit TreeNode Databindings...&lt;/i&gt;. &lt;/p&gt;
&lt;p&gt;&lt;img alt="DataBinding Editor Treeview" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIV_2.gif" /&gt;&lt;/p&gt;
&lt;p&gt;I den fremkomne vizard er der bl.a. et vindue med et treeview af Xml-dokumentet der genereres af datakilden. Marker een af &lt;i&gt;employee&lt;/i&gt;-noderne, og klik på add. Noden vil da fremkomme på listen &lt;i&gt;Selected data bindings&lt;/i&gt;, og man kan angive værdier for de enkelte grene i træet. Vi skal her sætte egenskaben &lt;i&gt;TextField&lt;/i&gt; og &lt;i&gt;ValueField&lt;/i&gt; til henholdsvis &lt;i&gt;Name&lt;/i&gt; og &lt;i&gt;ID&lt;/i&gt;. Læg mærke til at designeren selv foreslår værdierne fra XmlDataSourcen...nice. Nu skal vi så bare lige havde tilføjet noget sukker til vores layout, og det gøres nemt med Smarttag-punktet &lt;i&gt;Autoformat&lt;/i&gt; - her vælger jeg formatet &lt;i&gt;Contacts&lt;/i&gt;, da det giver et Messenger look, som egner sig fint til vores organisationstræ. Voilla mere skal der ikke til, og vi har et fuldt funktionelt treeview med vores &lt;span&gt;personhierarki&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Men hov - alle personerne ligger på samme niveau. Vi har glemt een ting - vi skulle transformerer Xml-data for at få &lt;span&gt;hierarkiet&lt;/span&gt;. Det gøres nemt via Xslt som vi har set tidligere, og vi kan bare angive stien på vores Xslt i XmlDataSource egenskaben &lt;i&gt;TransformFile&lt;/i&gt; (i mit tilfælde vil det være "~/Trees/Tree.xsl"). &lt;/p&gt;&lt;img alt="Configure XmlDataSource" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIV_3.gif" /&gt; 
&lt;p&gt;Læg i øvrigt mærke til i XmlDatasource konfigurationen, at man kan angive et XPath udtryk, hvis man kun ønsker at bruge et delelement af datakilden. Nu er de rigtigt indlejrede under hinanden som de skal, og det ser sådan ud:.  &lt;/p&gt;&lt;img alt="TreeIV demo" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIV_4.gif" /&gt; 
&lt;p&gt;For lige at runde af og opsummerer hvad der skal til for at vise en træstruktur i ASP.NET 2.0.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lav et udtræk af data som indeholder parentid'er 
&lt;/li&gt;&lt;li&gt;Brug det som datakilde for XmlDataSource kontrollen 
&lt;/li&gt;&lt;li&gt;Tilføj den rekursive Xslt der danner &lt;span&gt;hierarkiet&lt;/span&gt; 
&lt;/li&gt;&lt;li&gt;Placer en TreeView kontrol på formen, og angiv XmlDataSource kontrollen som datakilde 
&lt;/li&gt;&lt;li&gt;Lav binding til Xml attributterne der indeholder navn og id&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Læg mærke til at alt sker i designeren, og alt angives deklarativt i Aspx-filen - der skrives ikke een linie kode i code-behind. Det kalder jeg abstraheret udvikling.&lt;/p&gt;
&lt;p&gt;Code on (eller skulle man sige 'Drag'n drop on')&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/495.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Her kommer den foreløbigt sidste post i min lille serie om træstrukturer (<a href="http://activedeveloper.dk/weblogs/mrjs/category/57.aspx">index</a>).</p>
<p>Nu er vi bevæbnet til tænderne med metoder til generering af træstrukturer i XML, og vi har netop downloadet beta 2 af VS.NET 2005. Skulle vi så ikke tage et kik på et par af de nye kontroller, som er blevet introduceret til os med ASP.NET 2.0, og se om ikke kan fremstille nogle flotte træstrukturer.</p>
<p>Start med at lave et nyt websted (eller forsæt på et eksisterende), og tilføj en ny webform. På formen slæber du en af de nye datakontroller ind: <i>XmlDataSource</i>-kontrollen. Den har en række egenskaber, men vi vil nøjes med at bruge den der hedder <i>DataFile</i>. Den angiver stien til Xml-filen der indeholder vores træstruktur fra tidligere (i mit web bliver det: "~/Trees/TreeData.xml"). Så slæber vi en webkontrol mere ind på siden: <i>TreeView</i>. I designeren fremkommer der et lille mærke (smarttag), som man kan aktiverer, og få adgang til en række vigtige vizards på den enkelte kontrol.</p><img alt="Smarttag i VS.NET 2005" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIV_1.gif" /> 
<p>På TreeView er der flere punkter, men den vigtigste er angivelsen af datakilden, som er repræsenteret ved punktet <i>Choose Data Source</i>. Ud for punktet er der en dropdownliste, som bl.a. indeholder vores XmlDataSource - den vælges. Her efter går vi videre til at skulle redigerer bindingen af vores datakilde til TreeView-delene. Det gøres via Smarttag-punktet <i>Edit TreeNode Databindings...</i>. </p>
<p><img alt="DataBinding Editor Treeview" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIV_2.gif" /></p>
<p>I den fremkomne vizard er der bl.a. et vindue med et treeview af Xml-dokumentet der genereres af datakilden. Marker een af <i>employee</i>-noderne, og klik på add. Noden vil da fremkomme på listen <i>Selected data bindings</i>, og man kan angive værdier for de enkelte grene i træet. Vi skal her sætte egenskaben <i>TextField</i> og <i>ValueField</i> til henholdsvis <i>Name</i> og <i>ID</i>. Læg mærke til at designeren selv foreslår værdierne fra XmlDataSourcen...nice. Nu skal vi så bare lige havde tilføjet noget sukker til vores layout, og det gøres nemt med Smarttag-punktet <i>Autoformat</i> - her vælger jeg formatet <i>Contacts</i>, da det giver et Messenger look, som egner sig fint til vores organisationstræ. Voilla mere skal der ikke til, og vi har et fuldt funktionelt treeview med vores <span>personhierarki</span>.</p>
<p>Men hov - alle personerne ligger på samme niveau. Vi har glemt een ting - vi skulle transformerer Xml-data for at få <span>hierarkiet</span>. Det gøres nemt via Xslt som vi har set tidligere, og vi kan bare angive stien på vores Xslt i XmlDataSource egenskaben <i>TransformFile</i> (i mit tilfælde vil det være "~/Trees/Tree.xsl"). </p><img alt="Configure XmlDataSource" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIV_3.gif" /> 
<p>Læg i øvrigt mærke til i XmlDatasource konfigurationen, at man kan angive et XPath udtryk, hvis man kun ønsker at bruge et delelement af datakilden. Nu er de rigtigt indlejrede under hinanden som de skal, og det ser sådan ud:.  </p><img alt="TreeIV demo" src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIV_4.gif" /> 
<p>For lige at runde af og opsummerer hvad der skal til for at vise en træstruktur i ASP.NET 2.0.</p>
<ol>
<li>Lav et udtræk af data som indeholder parentid'er 
</li><li>Brug det som datakilde for XmlDataSource kontrollen 
</li><li>Tilføj den rekursive Xslt der danner <span>hierarkiet</span> 
</li><li>Placer en TreeView kontrol på formen, og angiv XmlDataSource kontrollen som datakilde 
</li><li>Lav binding til Xml attributterne der indeholder navn og id</li></ol>
<p>Læg mærke til at alt sker i designeren, og alt angives deklarativt i Aspx-filen - der skrives ikke een linie kode i code-behind. Det kalder jeg abstraheret udvikling.</p>
<p>Code on (eller skulle man sige 'Drag'n drop on')</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/495.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Træer III</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/04/08/TreesIII.aspx</link><pubDate>Fri, 08 Apr 2005 09:39:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/04/08/TreesIII.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/421.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/04/08/TreesIII.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/421.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/421.aspx</trackback:ping><description>&lt;p&gt;I min blog, for et stykke tid siden, kom jeg til at skrive &lt;em&gt;"...og det kan selvfølgelig også gøres i ADO.NET, selvom der ikke er dynamiske cursorer her" &lt;/em&gt;(I posten &lt;a href="http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/29/387.aspx"&gt;Træer II&lt;/a&gt;).&lt;em&gt; &lt;/em&gt;Jeg forklarede om hvordan man genererede træer i ADO før ADO.NET, og der er venlige mennesker der har gjort mig opmærksom på at man kan gøre det samme i ADO.NET ved at bruge en &lt;em&gt;foreach&lt;/em&gt; på &lt;em&gt;Rows&lt;/em&gt; kollektionen uden dynamisk cursor (skriv endelig jeres forslag her på loggen, for så er der flere der får noget ud af det). Ja - det er rigtigt at man kan lave en rekursiv funktion der nester ind i &lt;em&gt;Rows&lt;/em&gt; kollektionen, men hvis man er ude efter at danne en hirakisk XML struktur, er der en langt lettere måde. Nemlig ved at udnytte muligheden for at bruge relationer i et &lt;em&gt;DataSet&lt;/em&gt;, og på den måde få ADO.NET til at lave alt det beskidte arbejde for os&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For eksemplets skyld tager vi det samme data, som jeg har brugt i tidligere indlæg:&lt;/p&gt;&lt;code&gt;SELECT * FROM employee ORDER BY BossID &lt;/code&gt;
&lt;p&gt;Det er rimeligt trivielt at indlæse data i et &lt;em&gt;DataSet&lt;/em&gt;, og jeg skal holde mig til kode der ikke er provider afhængig - kun klasser fra &lt;em&gt;System.Data.&lt;/em&gt; Det kan altså bruges med al data... også &lt;a href="http://www1.softwareag.com/Corporate/products/tamino/default.asp"&gt;Tamino&lt;/a&gt; ;-)&lt;/p&gt;
&lt;p&gt;Det første vi skal gøre er at frembringe relationen - &lt;em&gt;ParentChild&lt;/em&gt;:&lt;/p&gt;&lt;textarea style="WIDTH: 100%" rows="15"&gt;DataTable tbl = ds.Tables["employee"];
// Her går jeg ud fra at DataSet'et hedder ds, og tabellen employee.
DataRelation rel = new DataRelation(
                      "ParentChild", // Navn på relation
                      tbl.Columns["ID"], // parentColumn
                      tbl.Columns["BossID"], // childColumn
                      false /* vores datamodel siger at øverste bos er Null, så vi kan ikke gennemtvinge constrainten */
                     );
rel.Nested = true; // Det er her vi fortæller at resultatet skal være hirakisk
ds.Relations.Add( rel );
ds.WriteXml( Console.Out );&lt;/textarea&gt; 
&lt;p&gt;Og outputtet/resultatet:&lt;/p&gt;&lt;img src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIII.gif" /&gt; 
&lt;p&gt;Lige klar til XSLT eller som datakilde til en &lt;em&gt;&lt;a href="http://asp.net/ControlGallery/ControlDetail.aspx?Control=75&amp;amp;tabindex=2"&gt;TreeView&lt;/a&gt;.&lt;/em&gt;&lt;br /&gt;Ja - andet skal der ikke til i ADO.NET for at lave en hirakisk XML struktur - &lt;em&gt;DataRelation.Nested&lt;/em&gt; er din ven.&lt;br /&gt;Code on...&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/421.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>I min blog, for et stykke tid siden, kom jeg til at skrive <em>"...og det kan selvfølgelig også gøres i ADO.NET, selvom der ikke er dynamiske cursorer her" </em>(I posten <a href="http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/29/387.aspx">Træer II</a>).<em> </em>Jeg forklarede om hvordan man genererede træer i ADO før ADO.NET, og der er venlige mennesker der har gjort mig opmærksom på at man kan gøre det samme i ADO.NET ved at bruge en <em>foreach</em> på <em>Rows</em> kollektionen uden dynamisk cursor (skriv endelig jeres forslag her på loggen, for så er der flere der får noget ud af det). Ja - det er rigtigt at man kan lave en rekursiv funktion der nester ind i <em>Rows</em> kollektionen, men hvis man er ude efter at danne en hirakisk XML struktur, er der en langt lettere måde. Nemlig ved at udnytte muligheden for at bruge relationer i et <em>DataSet</em>, og på den måde få ADO.NET til at lave alt det beskidte arbejde for os<em>.</em></p>
<p>For eksemplets skyld tager vi det samme data, som jeg har brugt i tidligere indlæg:</p><code>SELECT * FROM employee ORDER BY BossID </code>
<p>Det er rimeligt trivielt at indlæse data i et <em>DataSet</em>, og jeg skal holde mig til kode der ikke er provider afhængig - kun klasser fra <em>System.Data.</em> Det kan altså bruges med al data... også <a href="http://www1.softwareag.com/Corporate/products/tamino/default.asp">Tamino</a> ;-)</p>
<p>Det første vi skal gøre er at frembringe relationen - <em>ParentChild</em>:</p><textarea style="WIDTH: 100%" rows="15">DataTable tbl = ds.Tables["employee"];
// Her går jeg ud fra at DataSet'et hedder ds, og tabellen employee.
DataRelation rel = new DataRelation(
                      "ParentChild", // Navn på relation
                      tbl.Columns["ID"], // parentColumn
                      tbl.Columns["BossID"], // childColumn
                      false /* vores datamodel siger at øverste bos er Null, så vi kan ikke gennemtvinge constrainten */
                     );
rel.Nested = true; // Det er her vi fortæller at resultatet skal være hirakisk
ds.Relations.Add( rel );
ds.WriteXml( Console.Out );</textarea> 
<p>Og outputtet/resultatet:</p><img src="/weblogs//images/activedeveloper_dk/mrjs/41/r_TreesIII.gif" /> 
<p>Lige klar til XSLT eller som datakilde til en <em><a href="http://asp.net/ControlGallery/ControlDetail.aspx?Control=75&amp;tabindex=2">TreeView</a>.</em><br />Ja - andet skal der ikke til i ADO.NET for at lave en hirakisk XML struktur - <em>DataRelation.Nested</em> er din ven.<br />Code on...</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/421.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Træer II</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/29/TreesII.aspx</link><pubDate>Tue, 29 Mar 2005 16:20:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/29/TreesII.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/387.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/29/TreesII.aspx#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/387.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/387.aspx</trackback:ping><description>&lt;p&gt;Som jeg nævnte i min blog for &lt;a href="http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/17/369.aspx"&gt;et par uger siden&lt;/a&gt;, vil jeg prøve at give et par tricks til håndtering af træstrukturer i XML. Jeg vil som nævnt tage udgangspunkt i træstrukturer der gemmes i en relationel database, som har mulighed for at oprette tabeller med selvreferencer - &lt;em&gt;Adjacency List modellen&lt;/em&gt;. Een af modelllens svage punkter er den manglende mulighed for udvælgelsen af en green i træstrukturen. Man kan godt udvælge en gren og dens umiddelbare undergrene, men så stopper det også, for man vil så skulle foretage en &lt;em&gt;sub-select&lt;/em&gt; for at finde yderligere børne-børn, og så videre - ikke særligt skalerbart. En måde at løse problemet på er ved at lave en XML cache med data fra tabellen, og transformerer det til hirakisk data via XSLT. Lad os tage datasættet fra tidligere:&lt;/p&gt;&lt;textarea style="WIDTH: 100%" rows="8"&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;
&amp;lt;staff&amp;gt;
 &amp;lt;employee id="1" name="Jesper" /&amp;gt;
 &amp;lt;employee id="2" bossid="1" name="Brian" /&amp;gt;
 &amp;lt;employee id="3" bossid="1" name="Per" /&amp;gt;
 &amp;lt;employee id="4" bossid="2" name="Ulla" /&amp;gt;
 &amp;lt;employee id="5" bossid="2" name="Pia" /&amp;gt;
&amp;lt;/staff&amp;gt;&lt;/textarea&gt; 
&lt;p&gt;Udvalgt fra databasen via følgende query: &lt;/p&gt;&lt;code&gt;SELECT * FROM employee ORDER BY BossID&lt;/code&gt; 
&lt;p&gt;I gamle dage (ASP 2+3), udlæste jeg selv data fra sådanne forespørgsler via dynamiske cursorer i ADO og rekursive funktioner, som kunne iterere gennem en hel træstruktur. Det kan være en krævende omgang for databasen, hvis der f.eks. er tale om en Access fil på et meget besøgt websted - script-timeout!! Det kan gøres meget lettere, og det kan selvfølgelig også gøres i ADO.NET, selvom der ikke er dynamiske cursorer her. Nemlig med XSLT. Det er faktisk ret lige til, når data er valid (altså ingen ring-referencer med nogen der er boss for en ansats boss), og kommer ud på ovenstående form. Det første man skal gøre i sin XSL er at sammensætte en forespørgsel &lt;em&gt;XPath Query&lt;/em&gt;, som returnerer rodelementet - i ovenstående data: Jesper. I modellen er værdien af BossID nullable, og sættes til null på den øverste boss. Bruger man den indbyggede funktion: &lt;em&gt;boolean(val)&lt;/em&gt;, validerer en null-værdi til false, og man skal så bare inverterer.&lt;/p&gt;&lt;code&gt;/staff/employee[not(boolean(@BossID))]&lt;/code&gt; 
&lt;p&gt;Når man så har den node, er det blot et spørgsmål om at lave et rekursivt kald til en template.:&lt;/p&gt;&lt;code&gt;/staff/employee[@BossID=$id]; //$id er en lokal variabel&lt;/code&gt; 
&lt;p&gt;Hvilket f.eks. kunne se således ud:&lt;/p&gt;&lt;textarea style="WIDTH: 100%" rows="16"&gt;&amp;lt;?xml version="1.0"?&amp;gt;
&amp;lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&amp;gt;
 &amp;lt;xsl:template match="/"&amp;gt;
  &amp;lt;staff&amp;gt;
   &amp;lt;xsl:apply-templates select="/staff/employee[not(boolean(@BossID))]" /&amp;gt;
  &amp;lt;/staff&amp;gt;
 &amp;lt;/xsl:template&amp;gt;
 &amp;lt;xsl:template match="employee"&amp;gt;
  &amp;lt;xsl:variable name="id" select="@ID" /&amp;gt;
  &amp;lt;emplyee id="{$id}" name="{@Name}"&amp;gt;
   &amp;lt;xsl:apply-templates select="/staff/employee[@BossID=$id]" /&amp;gt;
  &amp;lt;/emplyee&amp;gt;
 &amp;lt;/xsl:template&amp;gt;
&amp;lt;/xsl:stylesheet&amp;gt;&lt;/textarea&gt; 
&lt;p&gt;Læg mærke til &lt;em&gt;$id &lt;/em&gt;variabel - det gør jeg for ikke at få navne sammenfald i den rekursive XPath query. &lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;code&gt;/staff/employee[@BossID=@ID]&lt;/code&gt; 
&lt;p&gt;Ville slet ikke gå, dat det ville søge den node som havde enslydende attributværdi for BossID og ID - derfor erklæringen af variablen. Når ovenstående transformering udføres, fremkommer følgende XML:&lt;/p&gt;
&lt;p&gt;&lt;textarea style="WIDTH: 100%" rows="10"&gt;&amp;lt;?xml version="1.0"?&amp;gt;
&amp;lt;staff&amp;gt;
  &amp;lt;emplyee id="1" name="Jesper"&amp;gt;
    &amp;lt;emplyee id="2" name="Brian"&amp;gt;
      &amp;lt;emplyee id="4" name="Ulla"&amp;gt;&amp;lt;/emplyee&amp;gt;
      &amp;lt;emplyee id="5" name="Pia"&amp;gt;&amp;lt;/emplyee&amp;gt;
    &amp;lt;/emplyee&amp;gt;
    &amp;lt;emplyee id="3" name="Per"&amp;gt;&amp;lt;/emplyee&amp;gt;
  &amp;lt;/emplyee&amp;gt;
&amp;lt;/staff&amp;gt;
&lt;/textarea&gt;&lt;/p&gt;
&lt;p&gt;Hirakisk data (BossID er hermed overflødig ;-) ), som vi nemt kan lave forespørgsler ned i og få returneret hele under-elementer. Man kan evt. lave en funktion der opdaterer en ASP.NET cache med xml-strukturen, og på den måde lave sine søgninger ned i XMLDOM'en via XPath queryes. Søgningen mod toppen fra en given gren - breadcrumbs - er ligeledes let via resultatdokumentet, eller en transformering, som jeg beskrev i en artikl &lt;a href="http://activedeveloper.dk/weblogs/mrjs/articles/194.aspx"&gt;tidligere&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hvordan findes Ullas boss via XPath??? ;-)&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/387.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Som jeg nævnte i min blog for <a href="http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/17/369.aspx">et par uger siden</a>, vil jeg prøve at give et par tricks til håndtering af træstrukturer i XML. Jeg vil som nævnt tage udgangspunkt i træstrukturer der gemmes i en relationel database, som har mulighed for at oprette tabeller med selvreferencer - <em>Adjacency List modellen</em>. Een af modelllens svage punkter er den manglende mulighed for udvælgelsen af en green i træstrukturen. Man kan godt udvælge en gren og dens umiddelbare undergrene, men så stopper det også, for man vil så skulle foretage en <em>sub-select</em> for at finde yderligere børne-børn, og så videre - ikke særligt skalerbart. En måde at løse problemet på er ved at lave en XML cache med data fra tabellen, og transformerer det til hirakisk data via XSLT. Lad os tage datasættet fra tidligere:</p><textarea style="WIDTH: 100%" rows="8">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;staff&gt;
 &lt;employee id="1" name="Jesper" /&gt;
 &lt;employee id="2" bossid="1" name="Brian" /&gt;
 &lt;employee id="3" bossid="1" name="Per" /&gt;
 &lt;employee id="4" bossid="2" name="Ulla" /&gt;
 &lt;employee id="5" bossid="2" name="Pia" /&gt;
&lt;/staff&gt;</textarea> 
<p>Udvalgt fra databasen via følgende query: </p><code>SELECT * FROM employee ORDER BY BossID</code> 
<p>I gamle dage (ASP 2+3), udlæste jeg selv data fra sådanne forespørgsler via dynamiske cursorer i ADO og rekursive funktioner, som kunne iterere gennem en hel træstruktur. Det kan være en krævende omgang for databasen, hvis der f.eks. er tale om en Access fil på et meget besøgt websted - script-timeout!! Det kan gøres meget lettere, og det kan selvfølgelig også gøres i ADO.NET, selvom der ikke er dynamiske cursorer her. Nemlig med XSLT. Det er faktisk ret lige til, når data er valid (altså ingen ring-referencer med nogen der er boss for en ansats boss), og kommer ud på ovenstående form. Det første man skal gøre i sin XSL er at sammensætte en forespørgsel <em>XPath Query</em>, som returnerer rodelementet - i ovenstående data: Jesper. I modellen er værdien af BossID nullable, og sættes til null på den øverste boss. Bruger man den indbyggede funktion: <em>boolean(val)</em>, validerer en null-værdi til false, og man skal så bare inverterer.</p><code>/staff/employee[not(boolean(@BossID))]</code> 
<p>Når man så har den node, er det blot et spørgsmål om at lave et rekursivt kald til en template.:</p><code>/staff/employee[@BossID=$id]; //$id er en lokal variabel</code> 
<p>Hvilket f.eks. kunne se således ud:</p><textarea style="WIDTH: 100%" rows="16">&lt;?xml version="1.0"?&gt;
&lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 &lt;xsl:template match="/"&gt;
  &lt;staff&gt;
   &lt;xsl:apply-templates select="/staff/employee[not(boolean(@BossID))]" /&gt;
  &lt;/staff&gt;
 &lt;/xsl:template&gt;
 &lt;xsl:template match="employee"&gt;
  &lt;xsl:variable name="id" select="@ID" /&gt;
  &lt;emplyee id="{$id}" name="{@Name}"&gt;
   &lt;xsl:apply-templates select="/staff/employee[@BossID=$id]" /&gt;
  &lt;/emplyee&gt;
 &lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;</textarea> 
<p>Læg mærke til <em>$id </em>variabel - det gør jeg for ikke at få navne sammenfald i den rekursive XPath query. </p>
<p></p><code>/staff/employee[@BossID=@ID]</code> 
<p>Ville slet ikke gå, dat det ville søge den node som havde enslydende attributværdi for BossID og ID - derfor erklæringen af variablen. Når ovenstående transformering udføres, fremkommer følgende XML:</p>
<p><textarea style="WIDTH: 100%" rows="10">&lt;?xml version="1.0"?&gt;
&lt;staff&gt;
  &lt;emplyee id="1" name="Jesper"&gt;
    &lt;emplyee id="2" name="Brian"&gt;
      &lt;emplyee id="4" name="Ulla"&gt;&lt;/emplyee&gt;
      &lt;emplyee id="5" name="Pia"&gt;&lt;/emplyee&gt;
    &lt;/emplyee&gt;
    &lt;emplyee id="3" name="Per"&gt;&lt;/emplyee&gt;
  &lt;/emplyee&gt;
&lt;/staff&gt;
</textarea></p>
<p>Hirakisk data (BossID er hermed overflødig ;-) ), som vi nemt kan lave forespørgsler ned i og få returneret hele under-elementer. Man kan evt. lave en funktion der opdaterer en ASP.NET cache med xml-strukturen, og på den måde lave sine søgninger ned i XMLDOM'en via XPath queryes. Søgningen mod toppen fra en given gren - breadcrumbs - er ligeledes let via resultatdokumentet, eller en transformering, som jeg beskrev i en artikl <a href="http://activedeveloper.dk/weblogs/mrjs/articles/194.aspx">tidligere</a>.</p>
<p>Hvordan findes Ullas boss via XPath??? ;-)</p>
<p></p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/387.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Træer</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/17/TreesI.aspx</link><pubDate>Thu, 17 Mar 2005 22:09:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/17/TreesI.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/369.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/17/TreesI.aspx#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/369.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/369.aspx</trackback:ping><description>&lt;p&gt;Arbejder man med data, som på den ene eller anden måde skal gemmes som en træstruktur, har man principielt 3 mulige fremgangsmåder: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Nested Sets 
&lt;/li&gt;&lt;li&gt;Materialized Path 
&lt;/li&gt;&lt;li&gt;Adjacency List. &lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Så kan det godt være at der findes flere eller kombinationer, mendet er min overbevisning at der blot er tale om varianter af ovenstående 3 metoder (i gør mig opmærksom på det, hvis jeg tager fejl). &lt;/p&gt;
&lt;p&gt;&lt;em&gt;Nested Sets&lt;/em&gt; som &lt;a href="http://www.intelligententerprise.com/001020/celko.jhtml?_requestid=145525%5D"&gt;Joe Celko&lt;/a&gt; definerer dem, er nok den smukkeste model, og den har en lang række fordele i forhold til opslag i SQL data. Det er direkte muligt at udvælge en hel gren i een forespørgsel, finde stien til roden og såvidere (læs evt artiklen). Den eneste hage ved modellen, er at den er ret besværlig at indsætte nye grene - det kan i teorien være nødvendigt at opdaterer alle poster i et træ, bare for at indsætte een ny gren. Altså en god løsning til mere statisk data, men i praksis ubrugelig i mere dynamisk data, som f.eks. håndtering af forumtråde eller nyhedsgrupper. Det er ikke sjovt at skulle opdaterer alle poster i &lt;a title="Active Developer" href="/" target="_blank"&gt;AD&lt;/a&gt; forum hver gang men opretter en ny post. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;Materialized Path&lt;/em&gt; er nok den metode, som man først udtænker, når man bliver stillet over for opgaven at skulle gemme hirakisk data i SQL. Her har man en &lt;em&gt;primærnøgle&lt;/em&gt; af typen &lt;em&gt;vchar,&lt;/em&gt; som ud over at være nøgle også bærer information om postens placering i træstrukturen - f.eks.: '1.1.1.2'. Fordelen er at det er nemt at se på selve posten, hvor den befinder sig i hirakiet. Ulemperne opstår, når der skal slettes grene. Så skal der rekalkuleres og opdateres en lang række poster - dog virker træstrukturen i modsætning til føromtalte nestede sets stadig ved sletning, der er bare ikke længere orden i sagerne. Desuden er der en øvre grænse for antallet af niveauer som tabellen kan holde, da den skrevne sti bliver gemt i et textfelt, og da men ikke gør klogt i at gemme stien i et blob-felt pga. manglende index.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Adjacency List&lt;/em&gt; erden sidste, og nok også den mest brugte. Her består hirakieat af to kolonner: Id og ParentID (Name og BossName). Roden i hirakiet er så den post der ikke ar en værdi for &lt;em&gt;ParentID&lt;/em&gt;. Her er et banalt eksempel på et organisationsdiagram.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ID    BossID     Name&lt;br /&gt;==============================&lt;br /&gt;1     null         Jesper&lt;br /&gt;2     1            Brian&lt;br /&gt;3     1            Per&lt;br /&gt;4     2            Ulla&lt;br /&gt;5     2            Pia&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Heraf fremgår det at Brians chef hedder Jesper, og at han har to under sig; Ulla og Pia. Modellen har en stor fordel i forbindelse med indsættelse - man opretter blot en post med en given ParentID, og med det samme er posten indplaceret i hirakiet. Ved sletning, er man nød til at opdaterer alle poster der er direkte underordnede den slettede post, men det er en forholdsvis nem opgave. Problemerne opstår, når man vil lave en forespørgsel på en hel gren - 'Brian og alle under ham', eller når man ønsker at udvælge en hel kommandovej - 'Ullas vej til toppen'.&lt;/p&gt;
&lt;p&gt;Selv hælder jeg mest til &lt;em&gt;Adjacency List- &lt;/em&gt;metoden, og det er f.eks. også den model der ligger til grund for AD forum, som indeholder mere end 65000 indlæg. Det havde været umuligt i nogen af de andre modeller. Prøv at forestille dig at oprette et nyt indlæg via &lt;em&gt;Materialized Path&lt;/em&gt;-modellen: '1.23.54982.54987.65100' - hvilken størelse skal man sætte på feltet, og hvordan performer det med op imod 100000 poster.&lt;/p&gt;
&lt;p&gt;Check min blog ud senere, så vil jeg forklarer hvordan man kan udvidde &lt;em&gt;Adjacency List&lt;/em&gt; modellen med XML cache, som speeder udvælgelsen gevaldigt op, og gennemgå hvordan man kan bruge &lt;em&gt;CTE&lt;/em&gt; i &lt;em&gt;Yukon&lt;/em&gt; til noget af den samme funktionalitet. &lt;/p&gt;
&lt;p&gt;(Er der i øvrgt nogen der har links til nogle gode eks. på CTE og hirakier, så smid lige en post).&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/369.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Arbejder man med data, som på den ene eller anden måde skal gemmes som en træstruktur, har man principielt 3 mulige fremgangsmåder: </p>
<ol>
<li>Nested Sets 
</li><li>Materialized Path 
</li><li>Adjacency List. </li></ol>
<p>Så kan det godt være at der findes flere eller kombinationer, mendet er min overbevisning at der blot er tale om varianter af ovenstående 3 metoder (i gør mig opmærksom på det, hvis jeg tager fejl). </p>
<p><em>Nested Sets</em> som <a href="http://www.intelligententerprise.com/001020/celko.jhtml?_requestid=145525%5D">Joe Celko</a> definerer dem, er nok den smukkeste model, og den har en lang række fordele i forhold til opslag i SQL data. Det er direkte muligt at udvælge en hel gren i een forespørgsel, finde stien til roden og såvidere (læs evt artiklen). Den eneste hage ved modellen, er at den er ret besværlig at indsætte nye grene - det kan i teorien være nødvendigt at opdaterer alle poster i et træ, bare for at indsætte een ny gren. Altså en god løsning til mere statisk data, men i praksis ubrugelig i mere dynamisk data, som f.eks. håndtering af forumtråde eller nyhedsgrupper. Det er ikke sjovt at skulle opdaterer alle poster i <a title="Active Developer" href="/" target="_blank">AD</a> forum hver gang men opretter en ny post. </p>
<p><em>Materialized Path</em> er nok den metode, som man først udtænker, når man bliver stillet over for opgaven at skulle gemme hirakisk data i SQL. Her har man en <em>primærnøgle</em> af typen <em>vchar,</em> som ud over at være nøgle også bærer information om postens placering i træstrukturen - f.eks.: '1.1.1.2'. Fordelen er at det er nemt at se på selve posten, hvor den befinder sig i hirakiet. Ulemperne opstår, når der skal slettes grene. Så skal der rekalkuleres og opdateres en lang række poster - dog virker træstrukturen i modsætning til føromtalte nestede sets stadig ved sletning, der er bare ikke længere orden i sagerne. Desuden er der en øvre grænse for antallet af niveauer som tabellen kan holde, da den skrevne sti bliver gemt i et textfelt, og da men ikke gør klogt i at gemme stien i et blob-felt pga. manglende index.</p>
<p><em>Adjacency List</em> erden sidste, og nok også den mest brugte. Her består hirakieat af to kolonner: Id og ParentID (Name og BossName). Roden i hirakiet er så den post der ikke ar en værdi for <em>ParentID</em>. Her er et banalt eksempel på et organisationsdiagram.</p>
<p><code>ID    BossID     Name<br />==============================<br />1     null         Jesper<br />2     1            Brian<br />3     1            Per<br />4     2            Ulla<br />5     2            Pia</code></p>
<p>Heraf fremgår det at Brians chef hedder Jesper, og at han har to under sig; Ulla og Pia. Modellen har en stor fordel i forbindelse med indsættelse - man opretter blot en post med en given ParentID, og med det samme er posten indplaceret i hirakiet. Ved sletning, er man nød til at opdaterer alle poster der er direkte underordnede den slettede post, men det er en forholdsvis nem opgave. Problemerne opstår, når man vil lave en forespørgsel på en hel gren - 'Brian og alle under ham', eller når man ønsker at udvælge en hel kommandovej - 'Ullas vej til toppen'.</p>
<p>Selv hælder jeg mest til <em>Adjacency List- </em>metoden, og det er f.eks. også den model der ligger til grund for AD forum, som indeholder mere end 65000 indlæg. Det havde været umuligt i nogen af de andre modeller. Prøv at forestille dig at oprette et nyt indlæg via <em>Materialized Path</em>-modellen: '1.23.54982.54987.65100' - hvilken størelse skal man sætte på feltet, og hvordan performer det med op imod 100000 poster.</p>
<p>Check min blog ud senere, så vil jeg forklarer hvordan man kan udvidde <em>Adjacency List</em> modellen med XML cache, som speeder udvælgelsen gevaldigt op, og gennemgå hvordan man kan bruge <em>CTE</em> i <em>Yukon</em> til noget af den samme funktionalitet. </p>
<p>(Er der i øvrgt nogen der har links til nogle gode eks. på CTE og hirakier, så smid lige en post).</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/369.aspx" width = "1" height = "1" /></body></item></channel></rss>