I'm using XSLT 1.0. I have <RowBreak>
and <ColumnBreak>
elements in my XML file. RowBreak will group my data by row, while ColumnBreak will transform to columns. For as long as there's no match for ColumnBreak or RowBreak, the display should continue downwards.
Here is my XML:
<?xml version="1.0" encoding="utf-8" ?>
<Tree>
<Item>
<Label>Item 1</Label>
</Item>
<Item>
<Label>Item 2</Label>
</Item>
<ColumnBreak />
<Item>
<Label>Item 3</Label>
</Item>
<Item>
<Label>Item 4</Label>
</Item>
<Item>
<Label>Item 5</Label>
</Item>
<RowBreak />
<Item>
<Label>Item 6</Label>
</Item>
<Item>
<Label>Item 7</Label>
</Item>
<ColumnBreak />
<Item>
<Label>Item 8</Label>
</Item>
<RowBreak />
<Item>
<Label>Item 9</Label>
</Item>
<Item>
<Label>Item 10</Label>
</Item>
</Tree>
The expected output is this:
Item 1 Item 3
Item 2 Item 4
Item 5
Item 6 Item 8
Item 7
Item 9
Item 10
EDIT: Here's my updated XSLT. I still couldn't figure out how to do the nested grouping.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="yes"/>
<xsl:key name="rowGroups" match="Tree/*[not(self::RowBreak)]" use="count(preceding-sibling::RowBreak)" />
<xsl:template match="Tree">
<xsl:variable name="rowGroupings" select="*[not(self::RowBreak)][generate-id() = generate-id(key('rowGroups', count(preceding-sibling::RowBreak))[1])]" />
<xsl:variable name="position" select="position()" />
<table>
<xsl:for-each select="$rowGroupings">
<xsl:variable name="rowId" select="generate-id()"/>
<xsl:variable name="colGroupings" select="$rowGroupings[not(self::ColumnBreak)][generate-id()=$rowId][1]" />
<tr>
<td>
<xsl:for-each select="$colGroupings">
<xsl:sort select="$colGroupings[preceding-sibling::ColumnBreak]" order="descending" />
<xsl:if test="position() = 1">
<xsl:variable name="maxRows" select="$colGroupings[preceding-sibling::ColumnBreak]" />
<xsl:call-template name="ColumnGroupTemplate">
<xsl:with-param name="maxRows" select="$maxRows" />
<xsl:with-param name="colGroupings" select="$colGroupings" />
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="ColumnGroupTemplate">
<xsl:param name="maxRows" />
<xsl:param name="colGroupings" />
<xsl:for-each select="$maxRows">
<xsl:variable name="position" select="position()" />
<tr>
<xsl:for-each select="$colGroupings">
<xsl:variable name="group" select="$colGroupings[preceding-sibling::ColumnBreak]" />
<td>
<xsl:value-of select="normalize-space($colGroupings[position() = $position])" />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
How do I achieve the expected output using Muenchian grouping? Thanks!
If you apply this stylesheet against your input XML
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output method="html" version="5.0" />
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Tree">
<table>
<!-- process the first row -->
<xsl:call-template name="process_first_group">
<xsl:with-param name="group" select="*[not(preceding-sibling::RowBreak)
and not(self::RowBreak)]"/>
</xsl:call-template>
<xsl:apply-templates select="RowBreak"/>
</table>
</xsl:template>
<!-- succeeding rows that start with RowBreak -->
<xsl:template match="RowBreak">
<xsl:variable name="row_ID" select="generate-id()"/>
<xsl:variable name="prec_Col" select="count(preceding-sibling::ColumnBreak)"/>
<xsl:call-template name="process_other_group">
<xsl:with-param name="group" select="following-sibling::*[not(self::RowBreak)]
[preceding-sibling::RowBreak[1]
[generate-id()=$row_ID]
]"/>
<xsl:with-param name="row_ID" select="$row_ID"/>
<xsl:with-param name="prec_Col" select="$prec_Col"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="process_first_group">
<xsl:param name="group"/>
<xsl:for-each select="$group[self::Item[not(preceding-sibling::ColumnBreak)]]">
<xsl:variable name="pos" select="position()"/>
<tr>
<td>
<xsl:value-of select="."/>
</td>
<xsl:for-each select="following-sibling::ColumnBreak">
<xsl:variable name="col_ID" select="generate-id()"/>
<xsl:apply-templates select="$group[self::Item
[preceding-sibling::ColumnBreak[1]
[generate-id()=$col_ID]
]
[position()=$pos]
]"/>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:template>
<xsl:template name="process_other_group">
<xsl:param name="group"/>
<xsl:param name="row_ID"/>
<xsl:param name="prec_Col"/>
<xsl:for-each select="$group[self::Item[count(preceding-sibling::ColumnBreak)=$prec_Col]]">
<xsl:variable name="pos" select="position()"/>
<tr>
<td>
<xsl:value-of select="."/>
</td>
<xsl:for-each select="$group[self::ColumnBreak[preceding-sibling::RowBreak[generate-id()=$row_ID]]]">
<xsl:variable name="col_ID" select="generate-id()"/>
<xsl:apply-templates select="following-sibling::Item[preceding-sibling::ColumnBreak[1][generate-id()=$col_ID] and preceding-sibling::RowBreak[1][generate-id()=$row_ID]][position()=$pos]"/>
</xsl:for-each>
</tr>
</xsl:for-each>
</xsl:template>
<xsl:template match="Item">
<td><xsl:apply-templates/></td>
</xsl:template>
<xsl:template match="Label">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
you will get
<table>
<tr>
<td>Item 1</td>
<td>Item 3</td>
</tr>
<tr>
<td>Item 2</td>
</tr>
<tr>
<td>Item 4</td>
<td>Item 6</td>
<td>Item 8</td>
</tr>
<tr>
<td>Item 5</td>
<td>Item 7</td>
</tr>
<tr>
<td>Item 9</td>
</tr>
<tr>
<td>Item 10</td>
</tr>
</table>
See it in action https://xsltfiddle.liberty-development.net/3NzcBuj/1