This is the second article in a series focusing on developing XSLT-oriented applications using ASP.NET 2.0. Even though the series is based on ASP.NET 2.0, I use it only as a transformation engine. The entire focus will be on working with XSLT. You can use any transformation engine according to your requirements (such as Java or others).
In this article, I will be focusing on working with the “xsl:choose” construct available in XSLT. This article is heavily dependent on the first article of this series, especially when working with ASP.NET 2.0. If you come across any problems, I strongly suggest you go through my first article.
The entire solution (source code) for this article is available for free download (in the form of a zip). All the applications in this series have been developed using Microsoft Visual Studio 2005 Professional Edition on Microsoft Windows Server 2003 Standard Edition together with Microsoft SQL Server 2005 Developer Edition as the database. You can even directly copy and paste the code given in this article and save with extensions XML or XSL and work with your own XSL parsers/engines.
I didn’t really test any of the code in any other tools/IDEs/servers/editions/versions. If you have any problems, please feel free to post in the discussion area.
Setting up your environment
To work with this article, I set up a new database table called “emp” with the fields empno, ename, sal and deptno. I also inserted a few sample rows. Once you execute the following query using “getXMLContent” method:
select Empno,Ename,Sal,Deptno from dbo.emp
You are likely to receive the following XML upon the execution of above SELECT command:
<SQLData>
<Rows>
<Empno>1001</Empno>
<Ename>Jag</Ename>
<Sal>4400</Sal>
<Deptno>10</Deptno>
</Rows>
<Rows>
<Empno>1002</Empno>
<Ename>Chat</Ename>
<Sal>2800</Sal>
<Deptno>20</Deptno>
</Rows>
<Rows>
<Empno>1003</Empno>
<Ename>Winner</Ename>
<Sal>3700</Sal>
<Deptno>10</Deptno>
</Rows>
<Rows>
<Empno>1004</Empno>
<Ename>Dhan</Ename>
<Sal>5000</Sal>
<Deptno>20</Deptno>
</Rows>
</SQLData>
I also included a sample XSLT which works with the above XML document to test the accuracy of XML generation. The XSLT is as follows:
Once the above is executed, you should be given a list of all employee names. I also modified the code in such a way that it clearly lists the transformation. The following is the new code:
Dim xslt AsNew Xsl.XslCompiledTransform()
xslt.Load(Server.MapPath("XSLTFile11.xsl"))
' Create the writer.
Dim settings AsNew XmlWriterSettings()
settings.Indent = True
settings.IndentChars = ControlChars.Tab
settings.ConformanceLevel = ConformanceLevel.Auto
Dim sw AsNew IO.StringWriter
Dim writer As XmlWriter = XmlWriter.Create(sw, settings)
Now it is time to start “control flow” expressions within XSLT. The first construct I would like to concentrate on is “xsl:choose.”
The construct “xsl:choose” is very similar to the “select case” structure. You can have any number of conditions using “xsl:when” and finally you can have an “else” part using “xsl:otherwise.”
Let us work with a practical example. I would like to display all the employees along with salaries. I would also like to have salaries displayed in bold if they are above 3500. Now, there exists a condition to work with. Such types of conditions can be dealt with either by “xsl:choose” or “xsl:if” (based on the needs).
The following is the code which displays all employee details along with salaries in bold, if they are above 3500:
<?xmlversion="1.0"encoding="utf-8"?>
<xsl:stylesheetversion="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:templatematch="/">
<tableborder="1">
<xsl:apply-templatesselect="SQLData/Rows" />
</table>
</xsl:template>
<xsl:templatematch="SQLData/Rows">
<tr>
<td>
<xsl:value-ofselect="Empno"/>
</td>
<td>
<xsl:value-ofselect="Ename"/>
</td>
<td>
<xsl:apply-templatesselect="Sal" />
</td>
<td>
<xsl:value-ofselect="Deptno"/>
</td>
</tr>
</xsl:template>
<xsl:templatematch="Sal">
<xsl:choose>
<xsl:whentest="text() > '3500'">
<b>
<xsl:value-ofselect="."/>
</b>
</xsl:when>
<xsl:otherwise>
<xsl:value-ofselect="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Once the above is executed you are likely to get the following transformation:
<tableborder="1">
<tr>
<td>1001</td>
<td>Jag</td>
<td>
<b>4400</b>
</td>
<td>10</td>
</tr>
<tr>
<td>1002</td>
<td>Chat</td>
<td>2800</td>
<td>20</td>
</tr>
.
.
.
</table>
I excluded few of the lines for clarity. The next section will explain in detail the above XSLT code.
Let us understand the code step by step. Let us start with the following:
<xsl:templatematch="/">
<tableborder="1">
<xsl:apply-templatesselect="SQLData/Rows" />
</table>
</xsl:template>
From the above code fragment, you can easily understand that I am emitting a TABLE tag and the content of the TABLE tag is based on the template defined in each and every “SQLData/Rows.” The template for “SQLData/Rows” is defined as follows:
<xsl:templatematch="SQLData/Rows">
<tr>
<td>
<xsl:value-ofselect="Empno"/>
</td>
<td>
<xsl:value-ofselect="Ename"/>
</td>
<td>
<xsl:apply-templatesselect="Sal" />
</td>
<td>
<xsl:value-ofselect="Deptno"/>
</td>
</tr>
</xsl:template>
According to the above, you can understand that I am emitting a TR tag for every “SQLData/Rows.” Within the TR, I am also emitting a TD tag along with the content available in leaf nodes (like Empno, Ename etc.). The only exception is the following TD emission:
<td>
<xsl:apply-templatesselect="Sal" />
</td>
The above emits a TD tag but the content is based on the template defined separately for the “Sal” node. The template for the “Sal” node is defined as follows:
<xsl:templatematch="Sal">
<xsl:choose>
<xsl:whentest="text() > '3500'">
<b>
<xsl:value-ofselect="."/>
</b>
</xsl:when>
<xsl:otherwise>
<xsl:value-ofselect="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The above template may be necessary as we need to test for a condition on “Sal.” From the above code fragment, you can understand that a single “xsl:choose” can be embedded with any number of conditions using “xsl:when.” The “text()” is nothing but the content of “Sal” (in this case).
The text gets compared with a value of 3500 and emits the text with bold tags if the condition evaluates to true. If the condition evaluates to false, “xsl:otherwise” gets executed and it emits only the content (without any bold tags).
In all of the previous examples, I simply emitted HTML. In fact, you can not only emit HTML; you can also emit CSS based on your needs!
Let us modify the business need specified in the previous section. Now, I would like to have all the employee details listed with the salaries in red, if earnings are more than 3500. The following is the modified XSLT which accomplishes the same:
<?xmlversion="1.0"encoding="utf-8"?>
<xsl:stylesheetversion="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:templatematch="/">
<tableborder="1">
<xsl:apply-templatesselect="SQLData/Rows" />
</table>
</xsl:template>
<xsl:templatematch="SQLData/Rows">
<tr>
<td>
<xsl:value-ofselect="Empno"/>
</td>
<td>
<xsl:value-ofselect="Ename"/>
</td>
<xsl:apply-templatesselect="Sal" />
<td>
<xsl:value-ofselect="Deptno"/>
</td>
</tr>
</xsl:template>
<xsl:templatematch="Sal">
<xsl:choose>
<xsl:whentest="text() > '3500'">
<tdstyle="color: red">
<xsl:value-ofselect="."/>
</td>
</xsl:when>
<xsl:otherwise>
<td>
<xsl:value-ofselect="."/>
</td>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When the above code gets executed, you are likely to get the following transformation:
<tableborder="1">
<tr>
<td>1001</td>
<td>Jag</td>
<tdstyle="color: red">4400</td>
<td>10</td>
</tr>
<tr>
<td>1002</td>
<td>Chat</td>
<td>2800</td>
<td>20</td>
</tr>
.
.
.
</table>
The only trick I used from all of the above code is that I emitted the TD tag of “Sal” within its own template, together with CSS wherever appropriate!
It is recommended to use XML entity references within conditions, for better compatibility and bug-free compilation. In the following example, I am trying to have more than one condition within “xsl:when” and using entity references as conditional operators:
<?xmlversion="1.0"encoding="utf-8"?>
<xsl:stylesheetversion="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:templatematch="/">
<tableborder="1">
<xsl:apply-templatesselect="SQLData/Rows" />
</table>
</xsl:template>
<xsl:templatematch="SQLData/Rows">
<tr>
<td>
<xsl:value-ofselect="Empno"/>
</td>
<td>
<xsl:value-ofselect="Ename"/>
</td>
<xsl:apply-templatesselect="Sal" />
<td>
<xsl:value-ofselect="Deptno"/>
</td>
</tr>
</xsl:template>
<xsl:templatematch="Sal">
<xsl:choose>
<xsl:whentest="text() > '3000' and text() < '4000'">
<tdstyle="color: red">
<xsl:value-ofselect="."/>
</td>
</xsl:when>
<xsl:otherwise>
<td>
<xsl:value-ofselect="."/>
</td>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Within the above code “>” stands for “>” and “<” stands for “<”. Sometimes, you may also have to use “"” instead of a single quote! So, keep in mind that XML entity references play major role in XSLT.
I hope you enjoyed the article and any comments, suggestions, feedback, bugs, errors, enhancements etc. are highly appreciated at http://jagchat.spaces.live.com