xslt - XSL recursive traverse based on variable -
i'm trying head around xsl recursion. need build html table based on xml using xslt cell tags position mapped string attribute called name formatted "row.column".
i'm trying recursively start last cell , count first cell ( 0.0 ) , start building table. reason cells may span several cell positions. use in html cannot process cells in for-each loop. last cell identified table attributes; bodyrowcount , columncount.
the xml looks this
<table headerrowcount="0" footerrowcount="0" bodyrowcount="4" columncount="2" > <cell self="ucd8iceei0" name="0:0" rowspan="1" columnspan="1"> <content>1</content> </cell> <cell self="ucd8iceei1" name="1:0" rowspan="1" columnspan="1"> <content>2</content> </cell> <cell self="ucd8iceei2" name="2:0" rowspan="1" columnspan="2> <content>3</content> </cell> <cell self="ucd8iceei3" name="3:0" rowspan="1" columnspan="1"> <content>4</content> </cell> <cell self="ucd8iceei4" name="0:1" rowspan="1" columnspan="1"> <content>5</content> </cell> <cell self="ucd8iceei6" name="2:1" rowspan="1" columnspan="1"> <content>7</content> </cell> <cell self="ucd8iceei7" name="3:1" rowspan="1" columnspan="1"> <content>8</content> </cell> </table>
the result should
<table> <tr> <td>1</td> <td>5</td> </tr> <tr> <td> colspan="2">2</td> </tr> <tr> <td>3</td> <td>7</td> </tr> <tr> <td>4</td> <td>8</td> </tr> </table>
the xls in pseudo format below. tried pass arguments start in case @ cell name 4.2 seems illigal pass argument based on attributes, surely must possible?
<xsl:template match="table"> <table> <xsl:apply-templates select="cell[@name = concat(@bodyrowcount,':',columncount)']"/> </table> </xsl:template> <xsl:template name="cell[@name='0.0"> found first cell, start first row in table <tr><td colspan=<xsl:value-of select=(concat(3",@columnspan,3")) rowspan=<xsl:value-of select=(concat(3",@rowspan,3"))> row content here </td></tr> </xsl:template> <xsl:template name="cell[@name='0.*"> call template 'row-1, column max <xsl:apply-templates select="cell[@name = 'row-1:maxcolumn']"> new row goes here <tr><td colspan=<xsl:value-of select=(concat(3",@columnspan,3")) rowspan=<xsl:value-of select=(concat(3",@rowspan,3"))> row content here </td></tr> </xsl:template> <xsl:template name="cell[@name='*.*"> call template 1 column before <xsl:apply-templates select="cell[@name = same row:column-1']"> <td colspan=<xsl:value-of select=(concat(3",@columnspan,3")) rowspan=<xsl:value-of select=(concat(3",@rowspan,3"))> row content here </td> </xsl:template>
i looking on how start recursive loop in 4.2 go 0.0, build table traversing positions , inserting cell value if 1 exist ?
you don't need recursion this. xslt (1.0) way like:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform"> <xsl:key name="rows" match="cell" use="substring(@name,1,1)"/> <xsl:template match="table"> <table border="1"> <xsl:apply-templates select="cell[generate-id() = generate-id(key('rows',substring(@name,1,1)))]" mode="row"> <xsl:sort select="@name"/> </xsl:apply-templates> </table> </xsl:template> <xsl:template match="cell" mode="row"> <xsl:variable name="row" select="substring(@name,1,1)"/> <tr> <td> <xsl:if test="not(following-sibling::cell[substring(@name,1,1) = $row])"> <xsl:attribute name="colspan">2</xsl:attribute> </xsl:if> <xsl:value-of select="."/> </td> <xsl:apply-templates select="following-sibling::cell[substring(@name,1,1) = $row]" mode="secondcell"/> </tr> </xsl:template> <xsl:template match="cell" mode="secondcell"> <td> <xsl:value-of select="."/> </td> </xsl:template> </xsl:stylesheet>
how done?
- building key on rows information (first character of name attribute)
- apply template rows on found cells
- save first character of name attribute in variable
- check if following sibling same first character exists
- if not, add colspan (no need information xml, used)
- if so, apply template second cell
instead of <xsl:value-of select="."/> can use <xsl:apply-templates/>. thats not obvious beginners more straight xslt. purists demand on it.
edit @christian: key uses first character of attribute "name", returns 1 "cell" element each first character found. 1 each row, can figure out rows without having information explicit in xml tree. additional cells in row detected, if there following cells same first character.
edit @christian: xslt 2.0 solution ever shorter , doesn't need key:
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/xsl/transform" xmlns:xs="http://www.w3.org/2001/xmlschema" xmlns:fn="http://www.w3.org/2005/xpath-functions"> <xsl:template match="table"> <table border="1"> <xsl:for-each-group select="cell" group-by="substring(@name,1,1)"> <tr> <xsl:for-each select="current-group()"> <td> <xsl:if test="count(current-group())=1"> <xsl:attribute name="colspan">2</xsl:attribute> </xsl:if> <xsl:value-of select="."/> </td> </xsl:for-each> </tr> </xsl:for-each-group> </table> </xsl:template> </xsl:stylesheet>
Comments
Post a Comment