I got an XML source file (from other source) and its respective XSL file to get the XML transformed into Excel Spreadsheet.
What i need is to dynamically change background color of cell in spreadsheet based on XML attribute for a node using XSL
Example: Source.XML content
<workbooks>
<Wrkbook
<table Id="My table 1">
<Colnames>
<ColumnName>Student ID</ColumnName>
<ColumnName>Student Name</ColumnName>
<ColumnName>Subject 1</ColumnName>
<ColumnName>Subject 2</ColumnName>
<ColumnName>Subject 3</ColumnName>
<ColumnName>Subject 4</ColumnName>
<ColumnName>Subject 5</ColumnName>
<ColumnName>Subject 6</ColumnName>
</Colnames>
<Rows>
<CI>534</CI>
<CI>Ramu</CI>
<CI>67</CI>
<CI Colour="Green">67</CI>
<CI Colour="#e8e9e8">48</CI>
<CI>66</CI>
<CI Colour="#B3C389">39</CI>
<CI>67</CI>
</Rows>
<Rows>
<CI>534</CI>
<CI>Raul</CI>
<CI Colour="Green">63</CI>
<CI>89</CI>
<CI Colour="#007788">67</CI>
<CI>57</CI>
<CI>75</CI>
<CI Colour="#AABBCC">92</CI>
</Rows>
</table>
</Wrkbook>
</workbooks>
What i used to do earlier is i used to have a specific style for green and Yelow in XSL sheet for the attributes in their respective XML tags, hence i could manage it. But now the source file contains color codes and i need to highlight based on color code. XSL File :
<xsl:template match='/'>
<Workbook>
<Styles>
<Style ss:ID="Green">
<Borders>
<Border ss:Position="Bottom" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Left" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Right" ss:LineStyle="Continuous" ss:Weight="1"/>
<Border ss:Position="Top" ss:LineStyle="Continuous" ss:Weight="1"/>
</Borders>
<Font ss:FontName="Calibri" x:Family="Swiss" ss:Size="11"/>
<Interior ss:Color="#CCFFCC" ss:Pattern="Solid"/>
</Style>
</Styles>
<xsl:for-each select='workbooks'>
<xsl:for-each select='Wrkbook'>
<Worksheet ss:Name='Scores'>
<Table x:FullColumns='1' x:FullRows='1'>
<xsl:for-each select='table'>
<Row>
<xsl:for-each select='Colnames/ColumnName'>
<Cell ss:StyleID='s41'>
<Data ss:Type='String'>
<xsl:value-of select='.' disable-output-escaping='yes'/>
</Data>
</Cell>
</xsl:for-each>
</Row>
<xsl:for-each select='Rows'>
<Row>
<xsl:for-each select='CI'>
<xsl:choose>
<xsl:when test="@Colour = 'Green'">
<Cell ss:StyleID='Green'>
<Data ss:Type='String'>
<xsl:value-of select='.' disable-output-escaping='yes'/>
</Data>
</Cell>
</xsl:when>
<xsl:otherwise>
<Cell ss:StyleID='s38'>
<Data ss:Type='String'>
<xsl:value-of select='.' disable-output-escaping='yes'/>
</Data>
</Cell>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Row>
</xsl:for-each>
<Row>
<Cell></Cell>
</Row>
</xsl:for-each>
</Table>
</Worksheet>
</xsl:for-each>
</xsl:for-each>
</Workbook>
</xsl:template>
Please ignore if there are syntax errors in XSL, just i am looking for highlighting the spreadsheet color based on XML attribute dynamically.
Thank you!!
It's much easier to work with individual templates instead of many nested
for-each
elements. So I would suggest reorganizing the entire stylesheet so each template can deal with a part of the source tree. Also, since you have to generate additional data which doesn't follow the structure of the source tree, it would be appropriate to have named templates that you could call. I organized this answer in two parts. The second part will attempt to answer your question.Part 1: refactoring the stylesheet
In the template that matches root (
/
) you would place only the invariant basic structure of the file. Inside it you can use<apply-templates select="*/*/table"/>
so select the templates that will match the data in your table (skippingworkbooks
andWrkbook
). Since the header involves the creation of styles based on colours, we will deal with it later. The required<?mso-application progid="Excel.Sheet" ?>
processing instruction is also generated:The template for
table
is also very simple. Based on the stylesheet you provided, it consists on one<Row>
with data from theColnames/ColumnName
elements in your source XML, several<Row>
elements from each<Rows>
element, and one empty<Row>
containing an empty<Cell>
. This can be rewritten like this:There are two
<xsl:apply-templates/>
. The first one will call a template to build the first<Row>
containing severalColumnName
s. The second will build the individual data rows. This is the template which will be called for eachColumnName
:And this one will be called for each
Rows
:Again another
<xsl:aply-templates/>
. This one will process eachCI
element which will contain theCell
data. It will have to read theColour
attribute from each element and decide how to render it. Currently you only need to distinguishGreen
and nothing. We could rewrite it as:removing duplication and placing the cell printing in a named template, which we call with
<xsl:call-template>
:Part 2: Adding colour information
Now that the stylesheet is reorganized in smaller templates, we can proceed to adapt it so it will read colour information correctly.
First we should make a
key
containing all the colours that are avaliable:This will match all
Colour
attributes in allCI
elements. We can use the contents of theColour
attribute to select it.In the root template, we can build the
<Styles>
block by building aStyle
element for each colour. We will do that in a separate template, but first we need to iterate among the unique colours in your document. This can be done by using the Muenchian grouping technique in XSLT 1.0 (if you are using XSLT 2.0 you can handle this in a simpler way usingfor-each-group
):This will call the
make-style
template for each unique colour found in your source document. The template is being called with a parametercolour
which contains the current colour.We need IDs for each colour. The colour name could be used but the ID can't start with a hash, so one way to solve the problem is use the colour code removing the
#
. You can use the XPathtranslate()
function for that:translate($colour,'#','')
.Since your source data still uses the
Green
colour, and it doesn't follow that pattern, we have to deal with it separately. To set theStyle
ss:ID
attribute we need to do use the contents of the original attribute if the$colour
isGreen
and strip the#
otherwise:We also have to do that to build the
Interior
element:This is the final
make-style
template:Now we can refactor the
CI
template adding axsl:when
block to generate a cell with aStyleID
for the appropriate colour:This is the final stylesheet.
You can see the results and make adjustments in this XSLT fiddle.