Subject: Re: [xsl] Transform XML to HTML table with multiple columns and lines. From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx> Date: Tue, 6 Nov 2001 19:25:09 +0000 |
Hi Johan, > I use XP/SAX and Java to produce XML and XT to transform it to HTML. Damn. I was going to launch into a usual grouping-problem spiel, but since you use XT you can't use keys! My first recommendation is switching to a conformant XSLT processor (such as MSXML, Saxon or Xalan) - you'll probably find this kind of transformation a lot faster if you do. Anyway, this is a twist on a grouping problem. The first bit is easy: you need to create a table element for the Sales element, and a row for the heading which contains a static header for the Time column: <xsl:template match="Sales"> <table border="1"> <tr> <th>Time</th> ... </tr> ... </table> </xsl:template> Your first problem is how to generate the rest of the headers. You need to create a th element for each unique value of the Salesperson elements in the XML. Usually I'd recommend you find it using a key, but since you can't use keys, you can look at each Occasion and see, using the preceding-sibling:: axis, if there's already been an Occasion in the document that has involved the same Salesperson. If there hasn't been, then this is the first Occasion in the document, with a particular Salesperson, so you can use that Salesperson as the value of the header: <xsl:template match="Sales"> <xsl:variable name="salespeople" select="Occasion[not(preceding-sibling::Occasion/Salesperson = Salesperson)]/Salesperson" /> <table border="1"> <tr> <th>Time</th> <xsl:for-each select="$salespeople"> <th><xsl:value-of select="." /></th> </xsl:for-each> </tr> ... </table> </xsl:template> Now for the rest of the table. You want one row for each unique Time in the document. You can use the same kind of construction to get that as well: <xsl:template match="Sales"> <xsl:variable name="salespeople" select="Occasion[not(preceding-sibling::Occasion/Salesperson = Salesperson)]/Salesperson" /> <table border="1"> <tr> <th>Time</th> <xsl:for-each select="$salespeople"> <th><xsl:value-of select="." /></th> </xsl:for-each> </tr> <xsl:for-each select="Occasion[not(preceding-sibling::Occasion/Time = Time)]/Time"> <xsl:sort select="." /> <xsl:variable name="time" select="." /> <tr> <th><xsl:value-of select="$time" /></th> ... </tr> </xsl:for-each> </table> </xsl:template> For the cells, you need to cycle through the sales people again, and create a cell for each: <xsl:template match="Sales"> <xsl:variable name="salespeople" select="Occasion[not(preceding-sibling::Occasion/Salesperson = Salesperson)]/Salesperson" /> <table border="1"> <tr> <th>Time</th> <xsl:for-each select="$salespeople"> <th><xsl:value-of select="." /></th> </xsl:for-each> </tr> <xsl:for-each select="Occasion[not(preceding-sibling::Occasion/Time = Time)]/Time"> <xsl:sort select="." /> <xsl:variable name="time" select="." /> <tr> <th><xsl:value-of select="$time" /></th> <xsl:for-each select="$salespeople"> <td> <xsl:variable name="salesperson" select="." /> ... </td> </xsl:for-each> </tr> </xsl:for-each> </table> </xsl:template> The value of a cell is the Amount of the Occasion within the document that has the Time from the outer loop (which is in the $time variable) and the sales person from the inner loop (which is in the $salesperson variable). Add a variable definition at the top level of the template so that you can easily get to all the occasions, and then find the one with the correct sales person and time, as follows: <xsl:template match="Sales"> <xsl:variable name="occasions" select="Occasion" /> <xsl:variable name="salespeople" select="Occasion[not(preceding-sibling::Occasion/Salesperson = Salesperson)]/Salesperson" /> <table border="1"> <tr> <th>Time</th> <xsl:for-each select="$salespeople"> <th><xsl:value-of select="." /></th> </xsl:for-each> </tr> <xsl:for-each select="Occasion[not(preceding-sibling::Occasion/Time = Time)]/Time"> <xsl:sort select="." /> <xsl:variable name="time" select="." /> <tr> <th><xsl:value-of select="$time" /></th> <xsl:for-each select="$salespeople"> <td> <xsl:variable name="salesperson" select="." /> <xsl:value-of select="$occasions[Salesperson = $salesperson and Time = $time]/Amount" /> </td> </xsl:for-each> </tr> </xsl:for-each> </table> </xsl:template> You will probably find it a lot more efficient using keys; if you swap processors, have a look at http://www.jenitennison.com/xslt/grouping/muenchian.html for the equivalent of the path for selecting unique nodes. You should also use a key to retrieve the occasion from a given sales person and time, with a key declaration like: <xsl:key name="occasionsBySalespersonAndTime" match="Occasion" use="concat(Salesperson, '::', Time)" /> and retrieving the Occasion with: key('occasionsBySalespersonAndTime', concat($salesperson, '::', $time)) I hope that helps, Jeni --- Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
[xsl] Transform XML to HTML table w, Johan Andersson | Thread | RE: [xsl] Transform XML to HTML tab, Johan Andersson |
[xsl] lookup-table thoughts (was Re, Tom Myers | Date | RE: [xsl] lookup-table thoughts (wa, McKeever, Marty |
Month |