<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>XSL</title><link>http://activedeveloper.dk/weblogs/mrjs/category/42.aspx</link><description>XSL</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>Nxslt2</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/12/08/2142.aspx</link><pubDate>Thu, 08 Dec 2005 12:30:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/12/08/2142.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/2142.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/12/08/2142.aspx#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/2142.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/2142.aspx</trackback:ping><description>&lt;p&gt;&lt;a title="Signs of the Sand" href="http://www.tkachenko.com/blog/"&gt;Oleg Tkachenko&lt;/a&gt; har nu frigivet nxslt 2.0, som er et commandline værktøj til udførsel af Xsl transformeringer. Værktøjet er skrevet i Microsoft .NET Framework 2.0 og udnytter den nye super effektive &lt;em&gt;System.Xml.Xsl.XslCompiledTransform&lt;/em&gt;. Nu ved jeg godt at VS.NET 2005 leveres med en hidtil uovertruffen Xsl editor med mulighed for debug o.s.v., men der er tilfælde hvor dette værktøj har sin berettigelse. Har du f.eks. en applikaton, som producerer Xml filer på en server, så kan du nemt lave en Schedule, som tager denne Xml-fil og transformerer den med en Xsl, for til sidst at uploade resultatet som en Html-fil til en webserver.&lt;/p&gt;&lt;code&gt;c:\&amp;gt;nxslt2 source.xml style.xsl -o result.html&lt;/code&gt; 
&lt;p&gt;Er det ikke skønt at kunne sige: "værsgo her er e-handelsstatistikken vist på virksomhedens intranet!", til salgschefen?&lt;/p&gt;
&lt;p&gt;Ja det er jo kun fantasien der sætter grænsen, og det bedste af det hele: Oleg har frigivet nxsl2 på Open source under &lt;a title="Bsd Licens" href="http://www.xmllab.net/license.txt"&gt;BSD license&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hent den her: &lt;a title="Nxslt2 download" href="http://www.xmllab.net/Products/nxslt2/tabid/73/Default.aspx"&gt;Link&lt;/a&gt;&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/2142.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p><a title="Signs of the Sand" href="http://www.tkachenko.com/blog/">Oleg Tkachenko</a> har nu frigivet nxslt 2.0, som er et commandline værktøj til udførsel af Xsl transformeringer. Værktøjet er skrevet i Microsoft .NET Framework 2.0 og udnytter den nye super effektive <em>System.Xml.Xsl.XslCompiledTransform</em>. Nu ved jeg godt at VS.NET 2005 leveres med en hidtil uovertruffen Xsl editor med mulighed for debug o.s.v., men der er tilfælde hvor dette værktøj har sin berettigelse. Har du f.eks. en applikaton, som producerer Xml filer på en server, så kan du nemt lave en Schedule, som tager denne Xml-fil og transformerer den med en Xsl, for til sidst at uploade resultatet som en Html-fil til en webserver.</p><code>c:\&gt;nxslt2 source.xml style.xsl -o result.html</code> 
<p>Er det ikke skønt at kunne sige: "værsgo her er e-handelsstatistikken vist på virksomhedens intranet!", til salgschefen?</p>
<p>Ja det er jo kun fantasien der sætter grænsen, og det bedste af det hele: Oleg har frigivet nxsl2 på Open source under <a title="Bsd Licens" href="http://www.xmllab.net/license.txt">BSD license</a></p>
<p>Hent den her: <a title="Nxslt2 download" href="http://www.xmllab.net/Products/nxslt2/tabid/73/Default.aspx">Link</a></p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/2142.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Udskift apostrof i streng med Xsl</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/09/19/2003.aspx</link><pubDate>Mon, 19 Sep 2005 15:34:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/09/19/2003.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/2003.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/09/19/2003.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/2003.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/2003.aspx</trackback:ping><description>&lt;p&gt;Så stødte jeg ind i een af de der irriterende fejl med et Xslt som transformerer et Xml-datasæt til en Javascript array (du kender det nok...):&lt;/p&gt;
&lt;code&gt;
var myvar = '&amp;lt;xsl:value-of select="@name"/&amp;gt;';
&lt;/code&gt;
&lt;p&gt;Det går fint - lige ind til der er en bruger som begynder at putte apostrof ind i &lt;em&gt;@name&lt;/em&gt;. Så får vi fejl med uafsluttede strenge Javascript. Der findes ikke en &lt;em&gt;Replace&lt;/em&gt; funktion i Xsl, så vi må hen og være mere kreative. Jeg besluttede mig for at bruge funktionen &lt;em&gt;translate&lt;/em&gt;, som foruden strengen, også tager to parametre. Den første er den karakter som der skal søges efter, og den anden er den karakter, som der skal oversættes med (hvis disse parametre kun indeholder een karakter, er det det samme som en &lt;em&gt;replace&lt;/em&gt; funktionalitet. Jeg prøvede først med følgende:&lt;/p&gt;
&lt;code&gt;
var myvar = '&amp;lt;xsl:value-of select="translate(@name,'&amp;amp;apos;','\&amp;amp;apos;')"/&amp;gt;';
&lt;/code&gt;
&lt;p&gt;Hvilket gav uheldige Xsl-parserfejl, da &lt;em&gt;&amp;amp;apos;&lt;/em&gt; bliver tolket som en almindelig apostrof, og så er det jo pludselig Xsl'en som har uafsluttede strenge. Og min parameter nr 2: &lt;em&gt;\&amp;amp;apos;&lt;/em&gt; vil heller ikke virke, da translate kun kan erstatte een karakter med een karakter. Løsningen indbærer et lille kompromis. Ser man på karakteren apostrof (') og acute accent (´), så kan man se at de minder meget om hinnanden rent grafisk, og mit kompromis går ud på at jeg sagtens kan leve med acute i stedet for apostrof ;-). Det løser kun halvdelen af problemet. Problemet med uafsluttede strenge, kan løses ved at bytte rundt på dobbelt anførselstegn og apostrof. Det kan sagtens lade sig gøre, da begge dele er velformet Xml. Jeg ender så op med følgende Xslt:&lt;/p&gt;
&lt;code&gt;
var myvar = '&amp;lt;xsl:value-of select='translate(@name,"&amp;amp;apos;","´")'/&amp;gt;';
&lt;/code&gt;
&lt;p&gt;Så lykkedes det at få mine apostrof indeholdende strenge over i Javascript. Kan man ikke leve med acute i stedet for apostrof, kan man så lave en Javascript replace efterfølgende, men det er en helt anden historie.&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/2003.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Så stødte jeg ind i een af de der irriterende fejl med et Xslt som transformerer et Xml-datasæt til en Javascript array (du kender det nok...):</p>
<code>
var myvar = '&lt;xsl:value-of select="@name"/&gt;';
</code>
<p>Det går fint - lige ind til der er en bruger som begynder at putte apostrof ind i <em>@name</em>. Så får vi fejl med uafsluttede strenge Javascript. Der findes ikke en <em>Replace</em> funktion i Xsl, så vi må hen og være mere kreative. Jeg besluttede mig for at bruge funktionen <em>translate</em>, som foruden strengen, også tager to parametre. Den første er den karakter som der skal søges efter, og den anden er den karakter, som der skal oversættes med (hvis disse parametre kun indeholder een karakter, er det det samme som en <em>replace</em> funktionalitet. Jeg prøvede først med følgende:</p>
<code>
var myvar = '&lt;xsl:value-of select="translate(@name,'&amp;apos;','\&amp;apos;')"/&gt;';
</code>
<p>Hvilket gav uheldige Xsl-parserfejl, da <em>&amp;apos;</em> bliver tolket som en almindelig apostrof, og så er det jo pludselig Xsl'en som har uafsluttede strenge. Og min parameter nr 2: <em>\&amp;apos;</em> vil heller ikke virke, da translate kun kan erstatte een karakter med een karakter. Løsningen indbærer et lille kompromis. Ser man på karakteren apostrof (') og acute accent (´), så kan man se at de minder meget om hinnanden rent grafisk, og mit kompromis går ud på at jeg sagtens kan leve med acute i stedet for apostrof ;-). Det løser kun halvdelen af problemet. Problemet med uafsluttede strenge, kan løses ved at bytte rundt på dobbelt anførselstegn og apostrof. Det kan sagtens lade sig gøre, da begge dele er velformet Xml. Jeg ender så op med følgende Xslt:</p>
<code>
var myvar = '&lt;xsl:value-of select='translate(@name,"&amp;apos;","´")'/&gt;';
</code>
<p>Så lykkedes det at få mine apostrof indeholdende strenge over i Javascript. Kan man ikke leve med acute i stedet for apostrof, kan man så lave en Javascript replace efterfølgende, men det er en helt anden historie.</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/2003.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Url - hvad er det?</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/08/03/1481.aspx</link><pubDate>Wed, 03 Aug 2005 12:28:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/08/03/1481.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/1481.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/08/03/1481.aspx#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/1481.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/1481.aspx</trackback:ping><description>&lt;p&gt;Hvad er en Url? Ja, spørgsmålet lyder nok lidt banalt, men det er ikke desto mindre relevant. En kollega, som lige er begyndt at eksperimenterer med Xsl og Xml, ringede mig op, og var i vildrede over hvordan man skulle udfører en 'simpel' transformering af Xml data til Html.&lt;/p&gt;
&lt;p&gt;Han var rigtigt nok begyndt at kikke på &lt;em&gt;XslTransform&lt;/em&gt; objektet, men var så kommet i tvivl om hvilken af de mange overloads til &lt;em&gt;Load&lt;/em&gt; metoden man skulle bruge (ja - der er 11 overloads). Han var nået frem til at det var den som tog een streng som parameter, som var den mest simple. Dog synes han det var en streg i regningen, at denne parameter var en angivet som en Url, og at man på den måde var nød til at hoste Xsl-filen på en web-server. Jeg har selv brugt denne metode-overload 100 gange, og havde ærligt talt ikke hæftet mig specielt ved beskrivelsen af parameteren som Url.&lt;/p&gt;&lt;img title="VS.NET 2003 Intellisence" src="http://www.3wp.dk/WeblogFiles/03082005_1.gif" border="1" /&gt; 
&lt;p&gt;Jeg blev selv i tvivl, og overvejede hvad en XslTransform egentlig udfører, og hvad den måtte havde brug for for at kunne det. Det er selvfølgelig ikke kun et filnavn, men også en lokation af dette filnavn, som objektet skal bruge. Specielt når der skal løses link til eksterne ressourcer i Xsl'en. Men hvis man nu ikke skal bruge alt det 'fancy hejs', og kun skal bruge Xsl'en som template for noget data, og man bare vil havde filerne liggende i &lt;em&gt;C:/temp&lt;/em&gt;? Hvad skal man så med en Url?&lt;/p&gt;
&lt;p&gt;Her kommer vi så til pointen i dette indlæg: Hvad er en Url?&lt;/p&gt;
&lt;p&gt;Uniform Resource Locator - er hvad forkortelsen står for. Altså en &lt;em&gt;Entydig ressource lokation&lt;/em&gt;. Vi er så vandt til forkortelsen, og vi sætter automatisk en Url op i adresse-feltet i Internet Explorer med et Http:// foran. Http:// er dog ikke et nødvendigt element i en Url - det kan lige så godt være ftp:// eller File://. Det er jo bare angivelsen af protokollen, som skal bruges for at man kan hente den angivne fil på den angivne lokation. En Url hænger derfor ikke uløseligt sammen med en web-server, og den kan sagtens henvise til filsystemet.&lt;/p&gt;
&lt;p&gt;Bruger man &lt;a title="" href="http://www.aisto.com/roeder/dotnet"&gt;Lutz Roeders .NET Reflector&lt;/a&gt; på &lt;em&gt;XslTransform.Load&lt;/em&gt;, kan man se at den bruger en &lt;em&gt;XmlTextReader&lt;/em&gt; internt til at indlæse den angivne &lt;em&gt;Xsl&lt;/em&gt;, som igen bruger en &lt;em&gt;XmlResolver&lt;/em&gt; til at 'afkode' Url'en.&lt;/p&gt;
&lt;p&gt;Hvis man blot ønsker at transformerer data, kan man gøre det på følgende simple måde:&lt;/p&gt;&lt;code&gt;&lt;font size="2"&gt;XslTransform xt = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;new&lt;/font&gt;&lt;font size="2"&gt; XslTransform();&lt;br /&gt;xt.Load( @"c:\minXsl.xsl" );&lt;br /&gt;&lt;br /&gt;xt.Transform( @"c.\minData.xml", @"c:\mitHtml.html" );&lt;br /&gt;&lt;/font&gt;&lt;/code&gt;
&lt;p&gt;Vil man hellere foretage transformeringen i RAM, kan man gøre følgende:&lt;/p&gt;&lt;code&gt;&lt;font size="2"&gt;XslTransform xt = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;new&lt;/font&gt;&lt;font size="2"&gt; XslTransform();&lt;br /&gt;xt.Load( @"c:\minXsl.xsl" );&lt;br /&gt;&lt;br /&gt;XmlDocument doc = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;new&lt;/font&gt;&lt;font size="2"&gt; XmlDocument();&lt;br /&gt;doc.Load( @"c.\minData.xml" );&lt;br /&gt;&lt;br /&gt;StringWriter sw = &lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;new&lt;/font&gt;&lt;font size="2"&gt; StringWriter();&lt;br /&gt;xt.Transform( doc, (XsltArgumentList)&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;null&lt;/font&gt;&lt;font size="2"&gt;, sw, (XmlResolver)&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;null&lt;/font&gt;&lt;font size="2"&gt; );&lt;br /&gt;&lt;br /&gt;&lt;/font&gt;&lt;font color="#0000ff" size="2"&gt;string&lt;/font&gt;&lt;font size="2"&gt; html = sw.ToString();&lt;br /&gt;&lt;/font&gt;&lt;/code&gt;
&lt;p&gt;Altså - Url er en lokation.&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/1481.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Hvad er en Url? Ja, spørgsmålet lyder nok lidt banalt, men det er ikke desto mindre relevant. En kollega, som lige er begyndt at eksperimenterer med Xsl og Xml, ringede mig op, og var i vildrede over hvordan man skulle udfører en 'simpel' transformering af Xml data til Html.</p>
<p>Han var rigtigt nok begyndt at kikke på <em>XslTransform</em> objektet, men var så kommet i tvivl om hvilken af de mange overloads til <em>Load</em> metoden man skulle bruge (ja - der er 11 overloads). Han var nået frem til at det var den som tog een streng som parameter, som var den mest simple. Dog synes han det var en streg i regningen, at denne parameter var en angivet som en Url, og at man på den måde var nød til at hoste Xsl-filen på en web-server. Jeg har selv brugt denne metode-overload 100 gange, og havde ærligt talt ikke hæftet mig specielt ved beskrivelsen af parameteren som Url.</p><img title="VS.NET 2003 Intellisence" src="http://www.3wp.dk/WeblogFiles/03082005_1.gif" border="1" /> 
<p>Jeg blev selv i tvivl, og overvejede hvad en XslTransform egentlig udfører, og hvad den måtte havde brug for for at kunne det. Det er selvfølgelig ikke kun et filnavn, men også en lokation af dette filnavn, som objektet skal bruge. Specielt når der skal løses link til eksterne ressourcer i Xsl'en. Men hvis man nu ikke skal bruge alt det 'fancy hejs', og kun skal bruge Xsl'en som template for noget data, og man bare vil havde filerne liggende i <em>C:/temp</em>? Hvad skal man så med en Url?</p>
<p>Her kommer vi så til pointen i dette indlæg: Hvad er en Url?</p>
<p>Uniform Resource Locator - er hvad forkortelsen står for. Altså en <em>Entydig ressource lokation</em>. Vi er så vandt til forkortelsen, og vi sætter automatisk en Url op i adresse-feltet i Internet Explorer med et Http:// foran. Http:// er dog ikke et nødvendigt element i en Url - det kan lige så godt være ftp:// eller File://. Det er jo bare angivelsen af protokollen, som skal bruges for at man kan hente den angivne fil på den angivne lokation. En Url hænger derfor ikke uløseligt sammen med en web-server, og den kan sagtens henvise til filsystemet.</p>
<p>Bruger man <a title="" href="http://www.aisto.com/roeder/dotnet">Lutz Roeders .NET Reflector</a> på <em>XslTransform.Load</em>, kan man se at den bruger en <em>XmlTextReader</em> internt til at indlæse den angivne <em>Xsl</em>, som igen bruger en <em>XmlResolver</em> til at 'afkode' Url'en.</p>
<p>Hvis man blot ønsker at transformerer data, kan man gøre det på følgende simple måde:</p><code><font size="2">XslTransform xt = </font><font color="#0000ff" size="2">new</font><font size="2"> XslTransform();<br />xt.Load( @"c:\minXsl.xsl" );<br /><br />xt.Transform( @"c.\minData.xml", @"c:\mitHtml.html" );<br /></font></code>
<p>Vil man hellere foretage transformeringen i RAM, kan man gøre følgende:</p><code><font size="2">XslTransform xt = </font><font color="#0000ff" size="2">new</font><font size="2"> XslTransform();<br />xt.Load( @"c:\minXsl.xsl" );<br /><br />XmlDocument doc = </font><font color="#0000ff" size="2">new</font><font size="2"> XmlDocument();<br />doc.Load( @"c.\minData.xml" );<br /><br />StringWriter sw = </font><font color="#0000ff" size="2">new</font><font size="2"> StringWriter();<br />xt.Transform( doc, (XsltArgumentList)</font><font color="#0000ff" size="2">null</font><font size="2">, sw, (XmlResolver)</font><font color="#0000ff" size="2">null</font><font size="2"> );<br /><br /></font><font color="#0000ff" size="2">string</font><font size="2"> html = sw.ToString();<br /></font></code>
<p>Altså - Url er en lokation.</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/1481.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Paging med Xsl</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/07/06/1384.aspx</link><pubDate>Wed, 06 Jul 2005 15:36:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/07/06/1384.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/1384.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/07/06/1384.aspx#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/1384.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/1384.aspx</trackback:ping><description>&lt;p&gt;Jeg har i dag fulgt en &lt;a title="Tråd i forum på ActiveDeveloper.dk" href="/forum/forum.asp?mode=message&amp;amp;mid=73474&amp;amp;foid=19" target="_blank"&gt;tråd&lt;/a&gt; på &lt;a title="Active Developer" href="/" target="_blank"&gt;ActiveDeveloper.dk&lt;/a&gt;, hvor &lt;a title="ossi 05" href="/community/profil.asp?id=14187" target="_blank"&gt;ossi 05&lt;/a&gt; spørger til hvordan man laver paging i Xsl. Det undre mig lidt at der ikke er noget der har beskrevet dette scenarie tidligere (Google gav intet brugbart, og der er absolut intet på topXML.com!!!). Derfor laver jeg lige denne lille post om emnet.&lt;/p&gt;
&lt;p&gt;Jeg tager udgangspunkt i noget Xml-data som frembringes ved en eksport af &lt;em&gt;Produkter&lt;/em&gt;-tabellen i &lt;em&gt;Northwind.mdb&lt;/em&gt;. Data ser således ud:&lt;/p&gt;&lt;textarea style="WIDTH: 100%" rows="5"&gt;&amp;lt;dataroot&amp;gt;
    &amp;lt;produkter&amp;gt;
        &amp;lt;produktnr&amp;gt;1&amp;lt;/produktnr&amp;gt;
        &amp;lt;produktnavn&amp;gt;Chai&amp;lt;/produktnavn&amp;gt;
        &amp;lt;leverandã¸rnr&amp;gt;7&amp;lt;/leverandã¸rnr&amp;gt;
        &amp;lt;kategorinr&amp;gt;2&amp;lt;/kategorinr&amp;gt;
    &amp;lt;/produkter&amp;gt;
&amp;lt;/dataroot&amp;gt;
&lt;/textarea&gt; 
&lt;p&gt;For at foretage paging, skal vi havde et par oplysninger: Hvilken side er vi på (CurrentPage)? Hvor mange poster er der i alt (Count)? Hvor mange poster er der på hver side (PageSize)? Disse oplysninger skal vi bruge, for at kunne begrænse udlæsningen, og for at kunne udlæse lige netop de poster som ønskes. Count og PageSize er egentlige konstanter for hele pagineringen, og CurrentPage er variablen der ændres fra side til side. Desuden har vi behov for et entydigt indeks, for at kunne finde frem til hvor i vores data vi er, og til det bruger jeg &lt;em&gt;XPath&lt;/em&gt;-funktionen &lt;em&gt;position()&lt;/em&gt;, som altid returnerer hvilken position den aktuelle node har i det aktuelle node-set. Med det på plads, kan jeg altså opsætte følgende filter udtryk:&lt;/p&gt;&lt;code&gt;position()&amp;gt;(($CurrentPage -1)*$PageSize) and position()&amp;lt;((($CurrentPage -1)*$PageSize)+$PageSize)&lt;/code&gt; 
&lt;p&gt;Bemærk at man er nød til at trække een fra CurrentPage, da ikke &lt;em&gt;position()&lt;/em&gt; returnerer et 0-baseret indeks. Jeg skal så bare indføje filtret i mit &lt;em&gt;xsl:apply-templates&lt;/em&gt; element.&lt;/p&gt;&lt;textarea style="WIDTH: 100%" rows="25"&gt;&amp;lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&amp;gt;
 &amp;lt;xsl:param name="CurrentPage" select="0" /&amp;gt;
 &amp;lt;xsl:param name="PageSize" select="10" /&amp;gt;
 &amp;lt;xsl:output method="html" indent="yes" /&amp;gt;
 &amp;lt;xsl:template match="/"&amp;gt;
  &amp;lt;table&amp;gt;
   &amp;lt;xsl:apply-templates select="//Produkter[position()&amp;gt;(($CurrentPage -1)*$PageSize) and position()&amp;lt;((($CurrentPage -1)*$PageSize)+$PageSize)]" /&amp;gt;
  &amp;lt;/table&amp;gt;
 &amp;lt;/xsl:template&amp;gt;
 &amp;lt;xsl:template match="Produkter"&amp;gt;
  &amp;lt;tr&amp;gt;
   &amp;lt;td&amp;gt;
    &amp;lt;xsl:value-of select="Produktnr" /&amp;gt;
   &amp;lt;/td&amp;gt;
   &amp;lt;td&amp;gt;
    &amp;lt;xsl:value-of select="Produktnavn" /&amp;gt;
   &amp;lt;/td&amp;gt;
  &amp;lt;/tr&amp;gt;
 &amp;lt;/xsl:template&amp;gt;
&amp;lt;/xsl:stylesheet&amp;gt;
&lt;/textarea&gt; 
&lt;p&gt;Det begrænser outputtet til kun at indeholde poster fra den ønskede side.&lt;/p&gt;
&lt;p&gt;For at danne traditionelle navigations-links kan man bruge en rekursiv funktion. Funktion skal bare kaldes med oplysninger om totalt antal sider og aktuel side, og herefter gentage sig selv til grænsen er nået. Du kan downloade den komplette Xsl fil her: &lt;a title="Xsl-fil" href="http://www.3wp.dk/WeblogFiles/Paging0.xsl" target="_blank"&gt;Paging0.xsl&lt;/a&gt;&lt;/p&gt;&lt;img title="Xsl paging i VS 2005 Xsl debugger" height="360" src="http://www.3wp.dk/WeblogFiles/070705_XslPaging.gif" width="297" border="0" /&gt; 
&lt;p&gt;Sådan, nu kan vi page i Xsl, og sådan ser det ud i vores nye &lt;em&gt;Xsl-debugger&lt;/em&gt; i Visual Studio 2005 - den er bare så &lt;strong&gt;fed&lt;/strong&gt;...&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/1384.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Jeg har i dag fulgt en <a title="Tråd i forum på ActiveDeveloper.dk" href="/forum/forum.asp?mode=message&amp;mid=73474&amp;foid=19" target="_blank">tråd</a> på <a title="Active Developer" href="/" target="_blank">ActiveDeveloper.dk</a>, hvor <a title="ossi 05" href="/community/profil.asp?id=14187" target="_blank">ossi 05</a> spørger til hvordan man laver paging i Xsl. Det undre mig lidt at der ikke er noget der har beskrevet dette scenarie tidligere (Google gav intet brugbart, og der er absolut intet på topXML.com!!!). Derfor laver jeg lige denne lille post om emnet.</p>
<p>Jeg tager udgangspunkt i noget Xml-data som frembringes ved en eksport af <em>Produkter</em>-tabellen i <em>Northwind.mdb</em>. Data ser således ud:</p><textarea style="WIDTH: 100%" rows="5">&lt;dataroot&gt;
    &lt;produkter&gt;
        &lt;produktnr&gt;1&lt;/produktnr&gt;
        &lt;produktnavn&gt;Chai&lt;/produktnavn&gt;
        &lt;leverandã¸rnr&gt;7&lt;/leverandã¸rnr&gt;
        &lt;kategorinr&gt;2&lt;/kategorinr&gt;
    &lt;/produkter&gt;
&lt;/dataroot&gt;
</textarea> 
<p>For at foretage paging, skal vi havde et par oplysninger: Hvilken side er vi på (CurrentPage)? Hvor mange poster er der i alt (Count)? Hvor mange poster er der på hver side (PageSize)? Disse oplysninger skal vi bruge, for at kunne begrænse udlæsningen, og for at kunne udlæse lige netop de poster som ønskes. Count og PageSize er egentlige konstanter for hele pagineringen, og CurrentPage er variablen der ændres fra side til side. Desuden har vi behov for et entydigt indeks, for at kunne finde frem til hvor i vores data vi er, og til det bruger jeg <em>XPath</em>-funktionen <em>position()</em>, som altid returnerer hvilken position den aktuelle node har i det aktuelle node-set. Med det på plads, kan jeg altså opsætte følgende filter udtryk:</p><code>position()&gt;(($CurrentPage -1)*$PageSize) and position()&lt;((($CurrentPage -1)*$PageSize)+$PageSize)</code> 
<p>Bemærk at man er nød til at trække een fra CurrentPage, da ikke <em>position()</em> returnerer et 0-baseret indeks. Jeg skal så bare indføje filtret i mit <em>xsl:apply-templates</em> element.</p><textarea style="WIDTH: 100%" rows="25">&lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 &lt;xsl:param name="CurrentPage" select="0" /&gt;
 &lt;xsl:param name="PageSize" select="10" /&gt;
 &lt;xsl:output method="html" indent="yes" /&gt;
 &lt;xsl:template match="/"&gt;
  &lt;table&gt;
   &lt;xsl:apply-templates select="//Produkter[position()&gt;(($CurrentPage -1)*$PageSize) and position()&lt;((($CurrentPage -1)*$PageSize)+$PageSize)]" /&gt;
  &lt;/table&gt;
 &lt;/xsl:template&gt;
 &lt;xsl:template match="Produkter"&gt;
  &lt;tr&gt;
   &lt;td&gt;
    &lt;xsl:value-of select="Produktnr" /&gt;
   &lt;/td&gt;
   &lt;td&gt;
    &lt;xsl:value-of select="Produktnavn" /&gt;
   &lt;/td&gt;
  &lt;/tr&gt;
 &lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;
</textarea> 
<p>Det begrænser outputtet til kun at indeholde poster fra den ønskede side.</p>
<p>For at danne traditionelle navigations-links kan man bruge en rekursiv funktion. Funktion skal bare kaldes med oplysninger om totalt antal sider og aktuel side, og herefter gentage sig selv til grænsen er nået. Du kan downloade den komplette Xsl fil her: <a title="Xsl-fil" href="http://www.3wp.dk/WeblogFiles/Paging0.xsl" target="_blank">Paging0.xsl</a></p><img title="Xsl paging i VS 2005 Xsl debugger" height="360" src="http://www.3wp.dk/WeblogFiles/070705_XslPaging.gif" width="297" border="0" /> 
<p>Sådan, nu kan vi page i Xsl, og sådan ser det ud i vores nye <em>Xsl-debugger</em> i Visual Studio 2005 - den er bare så <strong>fed</strong>...</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/1384.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Transformeringer bliver simplere</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/06/03/706.aspx</link><pubDate>Fri, 03 Jun 2005 08:52:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/06/03/706.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/706.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/06/03/706.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/706.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/706.aspx</trackback:ping><description>&lt;p&gt;Som tidligere &lt;a title="Link til tidligere post" href="/weblogs/mrjs/archive/2005/05/03/523.aspx"&gt;nævnt&lt;/a&gt;, så kommer System.Xml 2.0 med en helt ny XslTransform klasse, &lt;em&gt;XslCompiledTransform&lt;/em&gt;. Noget af det jeg har hæftet mig ved, ud over at den er hylende hurtig, er at den er blevet langt mere simpel at bruge. Ja der er lige de nævnte ting, omkring &lt;em&gt;document&lt;/em&gt;-funktionen og &lt;em&gt;script&lt;/em&gt;-blokke, men ser man bort fra det, så er der tale om et langt simplere api. Prøv for eks. at sammenlign den centrale metode, &lt;em&gt;Transform&lt;/em&gt;: &lt;em&gt;XslTransform&lt;/em&gt;'s metode har hele 18 overloads, hvor &lt;em&gt;XslCompiledTransform&lt;/em&gt; kun har 14. Reduceringen af antallet af metode-overloads, skyldes dels at man ikke længere har en overload som returnerer en &lt;em&gt;XmlReader&lt;/em&gt;, dels at &lt;em&gt;XPathNavigator&lt;/em&gt; nu implementerer &lt;em&gt;IXPathNavigable&lt;/em&gt; interfacet. Hvorfor har man ikke tænkt på det noget før?&lt;/p&gt;
&lt;p&gt;Nu er input til &lt;em&gt;Transform&lt;/em&gt; metoden altså reduceret til: &lt;em&gt;String&lt;/em&gt;, &lt;em&gt;XmlReader&lt;/em&gt;, eller &lt;em&gt;IXPathNavigable&lt;/em&gt; - nice!&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/706.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Som tidligere <a title="Link til tidligere post" href="/weblogs/mrjs/archive/2005/05/03/523.aspx">nævnt</a>, så kommer System.Xml 2.0 med en helt ny XslTransform klasse, <em>XslCompiledTransform</em>. Noget af det jeg har hæftet mig ved, ud over at den er hylende hurtig, er at den er blevet langt mere simpel at bruge. Ja der er lige de nævnte ting, omkring <em>document</em>-funktionen og <em>script</em>-blokke, men ser man bort fra det, så er der tale om et langt simplere api. Prøv for eks. at sammenlign den centrale metode, <em>Transform</em>: <em>XslTransform</em>'s metode har hele 18 overloads, hvor <em>XslCompiledTransform</em> kun har 14. Reduceringen af antallet af metode-overloads, skyldes dels at man ikke længere har en overload som returnerer en <em>XmlReader</em>, dels at <em>XPathNavigator</em> nu implementerer <em>IXPathNavigable</em> interfacet. Hvorfor har man ikke tænkt på det noget før?</p>
<p>Nu er input til <em>Transform</em> metoden altså reduceret til: <em>String</em>, <em>XmlReader</em>, eller <em>IXPathNavigable</em> - nice!</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/706.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Fra attribut til element</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/05/25/622.aspx</link><pubDate>Wed, 25 May 2005 11:21:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/05/25/622.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/622.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/05/25/622.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/622.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/622.aspx</trackback:ping><description>&lt;p&gt;Jeg sidder for tiden og bøvler med en ASP 3.0 applikation, som skal havde tilføjet lidt funktionalitet. I den forbindelse laver jeg en del transformeringer af ADO &lt;em&gt;RecordSet&lt;/em&gt; objekter. Jeg gør det ved at kalde &lt;em&gt;Save &lt;/em&gt;metoden på &lt;em&gt;RecordSet&lt;/em&gt;, og tilføje et &lt;em&gt;XmlDocument&lt;/em&gt; som parameter - alt sammen meget let. Hvis du kender lidt til den xml der skrives ved brug af denne metode, så ved du også at den i princippet fræser alle rækker ud i hvert sit element, &lt;strong&gt;z:row&lt;/strong&gt;, med en attribut for hver kolonne i posten. Jeg havde så brug for at min output, havde et element for hver attribut. Hvordan er det så lige man udvælger alle attributter som et node-set? Jeg har gjort det før, men kan ikke huske syntaksen, og det fremgår ikke klart af mine Xslt referencer.&lt;/p&gt;
&lt;p&gt;Lidt asen og masen, og så kom jeg frem til følgende &lt;em&gt;XPath&lt;/em&gt; udtryk:&lt;/p&gt;&lt;textarea style="WIDTH: 100%" rows="5"&gt;&amp;lt;xsl:for-each select="@*"&amp;gt;
   &amp;lt;xsl:value-of select="name()" /&amp;gt;: &amp;lt;xsl:value-of select="." /&amp;gt;
&amp;lt;/xsl:for-each&amp;gt;&lt;/textarea&gt; 
&lt;p&gt;Simpelt? Ja, men for mig er syntaksen ulogisk, og jeg er nød til at oprette denne post, så ved jeg hvordan næste gang jeg glemmer det...&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/622.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Jeg sidder for tiden og bøvler med en ASP 3.0 applikation, som skal havde tilføjet lidt funktionalitet. I den forbindelse laver jeg en del transformeringer af ADO <em>RecordSet</em> objekter. Jeg gør det ved at kalde <em>Save </em>metoden på <em>RecordSet</em>, og tilføje et <em>XmlDocument</em> som parameter - alt sammen meget let. Hvis du kender lidt til den xml der skrives ved brug af denne metode, så ved du også at den i princippet fræser alle rækker ud i hvert sit element, <strong>z:row</strong>, med en attribut for hver kolonne i posten. Jeg havde så brug for at min output, havde et element for hver attribut. Hvordan er det så lige man udvælger alle attributter som et node-set? Jeg har gjort det før, men kan ikke huske syntaksen, og det fremgår ikke klart af mine Xslt referencer.</p>
<p>Lidt asen og masen, og så kom jeg frem til følgende <em>XPath</em> udtryk:</p><textarea style="WIDTH: 100%" rows="5">&lt;xsl:for-each select="@*"&gt;
   &lt;xsl:value-of select="name()" /&gt;: &lt;xsl:value-of select="." /&gt;
&lt;/xsl:for-each&gt;</textarea> 
<p>Simpelt? Ja, men for mig er syntaksen ulogisk, og jeg er nød til at oprette denne post, så ved jeg hvordan næste gang jeg glemmer det...</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/622.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>.NET 2.0 vender ryggen til XslTransform</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/05/03/523.aspx</link><pubDate>Tue, 03 May 2005 11:32:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/05/03/523.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/523.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/05/03/523.aspx#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/523.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/523.aspx</trackback:ping><description>&lt;p&gt;I beta 2 releasen af .NET 2.0, er &lt;em&gt;XslTransform&lt;/em&gt; klassen blevet markeret som obsolete - dvs. den virker, men der bliver ikke optimeret mere på den, og det anbefales at man undlader at bruge den.. Lidt kedeligt, når man nu har været glad for den klasse, og brugt den i vid udstrækning. Man skal dog ikke græde alt for meget, for der er kommet en afløser, som må siges at være &lt;em&gt;XslTransform&lt;/em&gt; på speed - nemlig &lt;em&gt;XslCompiledTransform&lt;/em&gt;. Den indeholder en lang række forbedringer, men den vigtiste er at den kompilerer Xslt'en ned i et mellemformat (lidt som CLR for C# og VB.NET), som så bliver cached og genbrugt. Klassen er desuden optimeret og meget hurtigere runtime. &lt;/p&gt;
&lt;p&gt;Hvor store ændringer skal man så udfører for at lave sin 1.x kode om til at spille under 2.0? Ikke ret meget vil jeg mene. Lad os tage et standar eksempel, hvor vi transformerer en Xml-kilde, og returnerer den til en stream (f.eks. i en &lt;em&gt;HttpHandler&lt;/em&gt;) i 1.1 kompatibel kode:&lt;/p&gt;&lt;code&gt;XslTransform transformer = new XslTransform();&lt;br /&gt;transformer.Load( "My.xsl" );&lt;br /&gt;&lt;br /&gt;XslArgumentList args = new XslArgumentList();&lt;br /&gt;args.Add( "ID", Request["ID"] );&lt;br /&gt;&lt;br /&gt;transformer.Transform( "MyData.xml", args, Response.OutputStream ); &lt;/code&gt;
&lt;p&gt;Bliver til:&lt;/p&gt;&lt;code&gt;XslCompiledTransform transformer = new XslCompiledTransform();&lt;br /&gt;transformer.Load( "My.xsl" );&lt;br /&gt;&lt;br /&gt;XslArgumentList args = new XslArgumentList();&lt;br /&gt;args.Add( "ID", Request["ID"] );&lt;br /&gt;&lt;br /&gt;transformer.Transform( "MyData.xml", args, Response.OutputStream ); &lt;/code&gt;
&lt;p&gt;Ikke den helt store ændring i forhold til 1.1. Som jeg ser det, er det mest besværlige, hvis man har Xslt'er der indeholder scripting, eller som bruger &lt;em&gt;document&lt;/em&gt;() funktionen, for den nye klasse har nemlig deaktiveret disse to som default, og man er nød til at fodre et &lt;em&gt;XsltSettings&lt;/em&gt; objekt med i &lt;em&gt;Load&lt;/em&gt; metoden for at aktiverer dem:&lt;/p&gt;&lt;code&gt;bool enablescripting = true; &lt;br /&gt;bool enableDocumentLoad = true; &lt;br /&gt;XsltSettings settings = new XsltSettings( enableDocumentLoad, enablescripting );&lt;br /&gt;&lt;br /&gt;transformer.Load( "My.xsl", settings, new XmlUrlResolver() ); &lt;/code&gt;
&lt;p&gt;Det skulle måske genovervejes hvor ofte man tillader scripting i sin applikation. Er der tale om en &lt;em&gt;ASP.NET &lt;/em&gt;applikation hvor man lader brugeren få adgang til sine Xslt-filer, jo i sin koncekvens er muligt at eksekverer c# kode på serveren.... Jeg har slet ikke tænkt på det tidligere, men det er da oplagt for enhver hacker ikke?&lt;/p&gt;
&lt;p&gt;Code on...&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/523.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>I beta 2 releasen af .NET 2.0, er <em>XslTransform</em> klassen blevet markeret som obsolete - dvs. den virker, men der bliver ikke optimeret mere på den, og det anbefales at man undlader at bruge den.. Lidt kedeligt, når man nu har været glad for den klasse, og brugt den i vid udstrækning. Man skal dog ikke græde alt for meget, for der er kommet en afløser, som må siges at være <em>XslTransform</em> på speed - nemlig <em>XslCompiledTransform</em>. Den indeholder en lang række forbedringer, men den vigtiste er at den kompilerer Xslt'en ned i et mellemformat (lidt som CLR for C# og VB.NET), som så bliver cached og genbrugt. Klassen er desuden optimeret og meget hurtigere runtime. </p>
<p>Hvor store ændringer skal man så udfører for at lave sin 1.x kode om til at spille under 2.0? Ikke ret meget vil jeg mene. Lad os tage et standar eksempel, hvor vi transformerer en Xml-kilde, og returnerer den til en stream (f.eks. i en <em>HttpHandler</em>) i 1.1 kompatibel kode:</p><code>XslTransform transformer = new XslTransform();<br />transformer.Load( "My.xsl" );<br /><br />XslArgumentList args = new XslArgumentList();<br />args.Add( "ID", Request["ID"] );<br /><br />transformer.Transform( "MyData.xml", args, Response.OutputStream ); </code>
<p>Bliver til:</p><code>XslCompiledTransform transformer = new XslCompiledTransform();<br />transformer.Load( "My.xsl" );<br /><br />XslArgumentList args = new XslArgumentList();<br />args.Add( "ID", Request["ID"] );<br /><br />transformer.Transform( "MyData.xml", args, Response.OutputStream ); </code>
<p>Ikke den helt store ændring i forhold til 1.1. Som jeg ser det, er det mest besværlige, hvis man har Xslt'er der indeholder scripting, eller som bruger <em>document</em>() funktionen, for den nye klasse har nemlig deaktiveret disse to som default, og man er nød til at fodre et <em>XsltSettings</em> objekt med i <em>Load</em> metoden for at aktiverer dem:</p><code>bool enablescripting = true; <br />bool enableDocumentLoad = true; <br />XsltSettings settings = new XsltSettings( enableDocumentLoad, enablescripting );<br /><br />transformer.Load( "My.xsl", settings, new XmlUrlResolver() ); </code>
<p>Det skulle måske genovervejes hvor ofte man tillader scripting i sin applikation. Er der tale om en <em>ASP.NET </em>applikation hvor man lader brugeren få adgang til sine Xslt-filer, jo i sin koncekvens er muligt at eksekverer c# kode på serveren.... Jeg har slet ikke tænkt på det tidligere, men det er da oplagt for enhver hacker ikke?</p>
<p>Code on...</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/523.aspx" width = "1" height = "1" /></body></item><item><dc:creator>Jesper Jensen</dc:creator><title>Gruppering af data med Xslt</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/05/02/518.aspx</link><pubDate>Mon, 02 May 2005 15:07:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/05/02/518.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/518.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/05/02/518.aspx#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/518.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/518.aspx</trackback:ping><description>&lt;p&gt;Når der tales om gruppering af data i Xslt, er den mest udbredte og effektive metode, at bruge XPath-funktionen &lt;i&gt;generate-id()&lt;/i&gt; sammen med et &lt;i&gt;xsl:key&lt;/i&gt; index (læs min artikel om nøgler &lt;a href="http://activedeveloper.dk/weblogs/mrjs/articles/158.aspx"&gt;her&lt;/a&gt;). Metoden kaldes også: &lt;em&gt;Muench's metode&lt;/em&gt; efter metodens udvikler Steve Muench. &lt;/p&gt;
&lt;p&gt;Metoden går i sin enkelthed ud på, at man udnytter &lt;em&gt;key&lt;/em&gt; funktionens retur kollektion, som kun indeholder een node (den første forekomst i DOM'en) af noder der matcher nøglen. Har vi f.eks. et Xml dokument med følgende struktur:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Produkter 
&lt;ul&gt;
&lt;li&gt;Produkt 
&lt;ul&gt;
&lt;li&gt;VareGruppe 
&lt;/li&gt;&lt;li&gt;VareNr 
&lt;/li&gt;&lt;li&gt;VareNavn&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;og ønsker en udlæsning af data, der laver en sektion for hver varegruppe. Ja så er metoden nem og hurtig i forhold til loops i &lt;em&gt;DataSet&lt;/em&gt; eller rekursive kald. Man starter med at lave sin nøgle, som skal beskrive det index der sorteres på - her &lt;em&gt;VareGruppe&lt;/em&gt;:&lt;/p&gt;&lt;textarea style="WIDTH: 100%" rows="1"&gt;&amp;lt;xsl:key name="varegruppe" match="/Produkter/Produkt" use="@VareGruppe" /&amp;gt;&lt;/textarea&gt; 
&lt;p&gt;Vores gennemløb skal så tage udgangspunkt i produktgrupperne med følgende &lt;em&gt;XPath&lt;/em&gt; forespørgsel i rod-context:&lt;/p&gt;
&lt;p&gt; &lt;textarea style="WIDTH: 100%" rows="1"&gt;/Produkter/Produkt[generate-id(.)=generate-id(key('varegruppe',@VareGruppe))]&lt;/textarea&gt;&lt;/p&gt;
&lt;p&gt;Husk at &lt;em&gt;XPath-funktionen generate-&lt;/em&gt;id returnerer en unik id i det aktuelle dokument. Hvert element i DOM'en kan altså indekseres ud fra denne id...smart. Ovenstående forespørgsel siger altså: "Alle produkter der har en unik-id der matcher nøglen's id (varegruppe key elementet). I praksis vil denne forespørgsel returnerer en kollektion af noder der har forskellige &lt;em&gt;VareGruppe &lt;/em&gt;attributter. Efterfølgende kan man så lave en indre forespørgsel, der tager udgangspunkt i &lt;em&gt;Varegruppe&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;textarea style="WIDTH: 100%" rows="1"&gt;&amp;lt;xsl:apply-templates select="key('varegruppe',@VareGruppe)" /&amp;gt;&lt;/textarea&gt;&lt;/p&gt;
&lt;p&gt;Jeg håber alle er med endnu, for det er her man kan styre sit output, da man selvfølgelig kan tilsætte &lt;em&gt;xsl:sort&lt;/em&gt; elementer for at finjusterer outputtet 100%. Her følger det Xslt som kæder ovenstående sammen.&lt;/p&gt;
&lt;p&gt;&lt;textarea style="WIDTH: 100%" rows="25"&gt;&amp;lt;?xml version="1.0" encoding="utf-8" ?&amp;gt;
&amp;lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"&amp;gt;
&amp;lt;!-- Vores Varegruppe indeks --&amp;gt;
&amp;lt;xsl:key name="varegruppe" match="/Produkter/Produkt" use="@VareGruppe" /&amp;gt;
&amp;lt;xsl:template match="/"&amp;gt;
  &amp;lt;xsl:for-each select="/Produkter/Produkt[generate-id(.)=generate-id(key('varegruppe',@VareGruppe))]"&amp;gt;
&amp;lt;!-- Udskriv varegruppen --&amp;gt;
&amp;lt;table&amp;gt;
  &amp;lt;caption&amp;gt;&amp;lt;xsl:value-of select="@VareGruppe" /&amp;gt;&amp;lt;/caption&amp;gt;
    &amp;lt;!-- Så vælger vi alle Produkter med varegruppen via vores key --&amp;gt;
    &amp;lt;xsl:apply-templates select="key('varegruppe',@VareGruppe)" /&amp;gt;
&amp;lt;/table&amp;gt;
  &amp;lt;/xsl:for-each&amp;gt;
&amp;lt;/xsl:template&amp;gt;

&amp;lt;xsl:template match="Produkt"&amp;gt;
&amp;lt;tr&amp;gt;
  &amp;lt;td&amp;gt;&amp;lt;xsl:value-of select="@VareNr"/&amp;gt;&amp;lt;/td&amp;gt;
  &amp;lt;td&amp;gt;&amp;lt;xsl:value-of select="@VareNavn"/&amp;gt;&amp;lt;/td&amp;gt;
&amp;lt;/tr&amp;gt;
&amp;lt;/xsl:template&amp;gt;

&amp;lt;/xsl:stylesheet&amp;gt;
&lt;/textarea&gt;&lt;/p&gt;
&lt;p&gt;Det var det hele - sådan sorterer man med Steve Muench's metode. Får du den først ind på rygraden, vil du aldrig slippe den igen.....&lt;/p&gt;
&lt;p&gt;Her finder du en række eks. på gruppering med metoden - &lt;a href="http://topxml.com/xsltStylesheets/xslt_grouping.asp"&gt;her&lt;/a&gt;&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/518.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>Når der tales om gruppering af data i Xslt, er den mest udbredte og effektive metode, at bruge XPath-funktionen <i>generate-id()</i> sammen med et <i>xsl:key</i> index (læs min artikel om nøgler <a href="http://activedeveloper.dk/weblogs/mrjs/articles/158.aspx">her</a>). Metoden kaldes også: <em>Muench's metode</em> efter metodens udvikler Steve Muench. </p>
<p>Metoden går i sin enkelthed ud på, at man udnytter <em>key</em> funktionens retur kollektion, som kun indeholder een node (den første forekomst i DOM'en) af noder der matcher nøglen. Har vi f.eks. et Xml dokument med følgende struktur:</p>
<ul>
<li>Produkter 
<ul>
<li>Produkt 
<ul>
<li>VareGruppe 
</li><li>VareNr 
</li><li>VareNavn</li></ul></li></ul></li></ul>
<p>og ønsker en udlæsning af data, der laver en sektion for hver varegruppe. Ja så er metoden nem og hurtig i forhold til loops i <em>DataSet</em> eller rekursive kald. Man starter med at lave sin nøgle, som skal beskrive det index der sorteres på - her <em>VareGruppe</em>:</p><textarea style="WIDTH: 100%" rows="1">&lt;xsl:key name="varegruppe" match="/Produkter/Produkt" use="@VareGruppe" /&gt;</textarea> 
<p>Vores gennemløb skal så tage udgangspunkt i produktgrupperne med følgende <em>XPath</em> forespørgsel i rod-context:</p>
<p> <textarea style="WIDTH: 100%" rows="1">/Produkter/Produkt[generate-id(.)=generate-id(key('varegruppe',@VareGruppe))]</textarea></p>
<p>Husk at <em>XPath-funktionen generate-</em>id returnerer en unik id i det aktuelle dokument. Hvert element i DOM'en kan altså indekseres ud fra denne id...smart. Ovenstående forespørgsel siger altså: "Alle produkter der har en unik-id der matcher nøglen's id (varegruppe key elementet). I praksis vil denne forespørgsel returnerer en kollektion af noder der har forskellige <em>VareGruppe </em>attributter. Efterfølgende kan man så lave en indre forespørgsel, der tager udgangspunkt i <em>Varegruppe</em>.</p>
<p><textarea style="WIDTH: 100%" rows="1">&lt;xsl:apply-templates select="key('varegruppe',@VareGruppe)" /&gt;</textarea></p>
<p>Jeg håber alle er med endnu, for det er her man kan styre sit output, da man selvfølgelig kan tilsætte <em>xsl:sort</em> elementer for at finjusterer outputtet 100%. Her følger det Xslt som kæder ovenstående sammen.</p>
<p><textarea style="WIDTH: 100%" rows="25">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
&lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"&gt;
&lt;!-- Vores Varegruppe indeks --&gt;
&lt;xsl:key name="varegruppe" match="/Produkter/Produkt" use="@VareGruppe" /&gt;
&lt;xsl:template match="/"&gt;
  &lt;xsl:for-each select="/Produkter/Produkt[generate-id(.)=generate-id(key('varegruppe',@VareGruppe))]"&gt;
&lt;!-- Udskriv varegruppen --&gt;
&lt;table&gt;
  &lt;caption&gt;&lt;xsl:value-of select="@VareGruppe" /&gt;&lt;/caption&gt;
    &lt;!-- Så vælger vi alle Produkter med varegruppen via vores key --&gt;
    &lt;xsl:apply-templates select="key('varegruppe',@VareGruppe)" /&gt;
&lt;/table&gt;
  &lt;/xsl:for-each&gt;
&lt;/xsl:template&gt;

&lt;xsl:template match="Produkt"&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;xsl:value-of select="@VareNr"/&gt;&lt;/td&gt;
  &lt;td&gt;&lt;xsl:value-of select="@VareNavn"/&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/xsl:template&gt;

&lt;/xsl:stylesheet&gt;
</textarea></p>
<p>Det var det hele - sådan sorterer man med Steve Muench's metode. Får du den først ind på rygraden, vil du aldrig slippe den igen.....</p>
<p>Her finder du en række eks. på gruppering med metoden - <a href="http://topxml.com/xsltStylesheets/xslt_grouping.asp">her</a></p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/518.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>Søgning i XML</title><link>http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/15/XSLsearch.aspx</link><pubDate>Tue, 15 Mar 2005 10:11:00 GMT</pubDate><guid>http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/15/XSLsearch.aspx</guid><wfw:comment>http://activedeveloper.dk/weblogs/mrjs/comments/358.aspx</wfw:comment><comments>http://activedeveloper.dk/weblogs/mrjs/archive/2005/03/15/XSLsearch.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://activedeveloper.dk/weblogs/mrjs/comments/commentRss/358.aspx</wfw:commentRss><trackback:ping>http://activedeveloper.dk/weblogs/mrjs/services/trackbacks/358.aspx</trackback:ping><description>&lt;p&gt;En kollega kom forbi med et lille XML søge problem. Hun havde en (enorm) XML fil, som skulle gennemsøges for forekomsten af en streng. Selve XML'en så således ud:&lt;/p&gt;&lt;textarea style="WIDTH: 100%" rows="5"&gt;&amp;lt;Data&amp;gt;
  &amp;lt;Key name="bla bla 1"&amp;gt;Noget tekst der kan indeholde ord og bogstaver&amp;lt;/Key&amp;gt;
  &amp;lt;Key name="bla bla 2"&amp;gt;Noget tekst der kan indeholde ord og bogstaver&amp;lt;/Key&amp;gt;
  &amp;lt;Key name="bla bla 3"&amp;gt;Noget tekst der kan indeholde ord og bogstaver&amp;lt;/Key&amp;gt;
&amp;lt;!-- Og så videre --&amp;gt;
&amp;lt;/Data&amp;gt;&lt;/textarea&gt; 
&lt;p&gt;Hendes fremgangsmåde gik ud på at læse hver node een for een, og checke med en &lt;em&gt;RegularExpression&lt;/em&gt; (applikationen havde XMLDom'en i hukommelsen i forvejen). Fremgangsmåden var god nok, men spørgsmålet var om det kunne gøres mere effektivt. Jo, når XML'en nu er i hukommelsen som et &lt;em&gt;XmlDocument&lt;/em&gt;, så er det jo nærliggende at transformerer sig ud af problemet. Man kan bruge den indbyggede XSL-funktion &lt;em&gt;contains( str1, str2 )&lt;/em&gt;, som tager to strenge, og returnerer &lt;em&gt;true&lt;/em&gt;, hvis den ene er indeholdt i den anden. På den måde kunne man opsætte et XPath udtryk, som foretager søgningen: &lt;/p&gt;&lt;code&gt;/Data/Key[contains(.,'søgestreng')]&lt;/code&gt; 
&lt;p&gt;Den vil returnerer et &lt;em&gt;nodeset&lt;/em&gt; med alle Key-elementer, som indeholder teksten &lt;em&gt;'søgestreng'. &lt;/em&gt;Fordelen er selvfølgelig at man direkte kan formaterer sit søgeresultat:&lt;/p&gt;
&lt;p&gt;&lt;textarea style="WIDTH: 100%" rows="12"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8" ?&amp;gt;
&amp;lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&amp;gt;
    &amp;lt;xsl:param name="QUERY"/&amp;gt;
&amp;lt;!-- $QUERY indeholder søgestrengen --&amp;gt;
    &amp;lt;xsl:template match="/"&amp;gt;
        &amp;lt;xsl:apply-templates select="/Data/Key[contains(.,$QUERY)]" mode="Result"/&amp;gt;
    &amp;lt;/xsl:template&amp;gt;
    &amp;lt;xsl:template match="Key" mode="Result"&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;xsl:value-of select="@name"/&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/xsl:template&amp;gt;
&amp;lt;/xsl:stylesheet&amp;gt;&lt;/textarea&gt;&lt;/p&gt;
&lt;p&gt;Ovenstående vil i dette tilfælde lave en LI-liste med navnene på de Key-elementer der indeholder $QUERY&lt;/p&gt;
&lt;p&gt;Wrooom - det speedede processen op. Vær opmærksom på at ovenstående kun er en god ide, hvis XML'en er i hukommelsen i forvejen, ellers vil det være langt mere effektivt at bruge een af de hurtige 'Fire-hose'-readers, som f.eks. &lt;em&gt;XmlReader&lt;/em&gt;.&lt;/p&gt;&lt;img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/358.aspx" width = "1" height = "1" /&gt;</description><body xmlns="http://www.w3.org/1999/xhtml"><p>En kollega kom forbi med et lille XML søge problem. Hun havde en (enorm) XML fil, som skulle gennemsøges for forekomsten af en streng. Selve XML'en så således ud:</p><textarea style="WIDTH: 100%" rows="5">&lt;Data&gt;
  &lt;Key name="bla bla 1"&gt;Noget tekst der kan indeholde ord og bogstaver&lt;/Key&gt;
  &lt;Key name="bla bla 2"&gt;Noget tekst der kan indeholde ord og bogstaver&lt;/Key&gt;
  &lt;Key name="bla bla 3"&gt;Noget tekst der kan indeholde ord og bogstaver&lt;/Key&gt;
&lt;!-- Og så videre --&gt;
&lt;/Data&gt;</textarea> 
<p>Hendes fremgangsmåde gik ud på at læse hver node een for een, og checke med en <em>RegularExpression</em> (applikationen havde XMLDom'en i hukommelsen i forvejen). Fremgangsmåden var god nok, men spørgsmålet var om det kunne gøres mere effektivt. Jo, når XML'en nu er i hukommelsen som et <em>XmlDocument</em>, så er det jo nærliggende at transformerer sig ud af problemet. Man kan bruge den indbyggede XSL-funktion <em>contains( str1, str2 )</em>, som tager to strenge, og returnerer <em>true</em>, hvis den ene er indeholdt i den anden. På den måde kunne man opsætte et XPath udtryk, som foretager søgningen: </p><code>/Data/Key[contains(.,'søgestreng')]</code> 
<p>Den vil returnerer et <em>nodeset</em> med alle Key-elementer, som indeholder teksten <em>'søgestreng'. </em>Fordelen er selvfølgelig at man direkte kan formaterer sit søgeresultat:</p>
<p><textarea style="WIDTH: 100%" rows="12">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    &lt;xsl:param name="QUERY"/&gt;
&lt;!-- $QUERY indeholder søgestrengen --&gt;
    &lt;xsl:template match="/"&gt;
        &lt;xsl:apply-templates select="/Data/Key[contains(.,$QUERY)]" mode="Result"/&gt;
    &lt;/xsl:template&gt;
    &lt;xsl:template match="Key" mode="Result"&gt;
        &lt;li&gt;&lt;xsl:value-of select="@name"/&gt;&lt;/li&gt;
    &lt;/xsl:template&gt;
&lt;/xsl:stylesheet&gt;</textarea></p>
<p>Ovenstående vil i dette tilfælde lave en LI-liste med navnene på de Key-elementer der indeholder $QUERY</p>
<p>Wrooom - det speedede processen op. Vær opmærksom på at ovenstående kun er en god ide, hvis XML'en er i hukommelsen i forvejen, ellers vil det være langt mere effektivt at bruge een af de hurtige 'Fire-hose'-readers, som f.eks. <em>XmlReader</em>.</p><img src ="http://activedeveloper.dk/weblogs/mrjs/aggbug/358.aspx" width = "1" height = "1" /></body></item></channel></rss>