by Muthu Kumar Arjunan of Sonata Software Ltd
This article explains how a generic xml file(generated from a Spreadsheet ML document) can be converted to a HTML table that could be displayed in a browser using XSLT and styled using CSS.
This article's intended audience includes:
a) Developers of heterogeneous applications
b) Developers who are interested in displaying Spreadsheet ML document in a browser.
c) All those who are interested in learning how to convert XML to HTML table.
Source code is downloadable.
Note: The downloadable (zip file) is an asp net application. Yet care has been taken to avoid usage of Microsoft’s native dlls on Open XML packaging (Sytem.IO.Packaging). Thus ensuring code conversion of downloadable (asp net application) to other heterogeneous platforms can be done with ease, if required.
Prerequisites: For a better understanding of the basic concepts involved, please review the previously posted article on “XSL transformation of Spreadsheet ML to generic XML” where Spreadsheet ML is converted to a generic XML format.
A sample generic XML format for Spreadsheet ML data:
<root >
<XLRow>
<A>QUARTERDESC</A>
<B>MONTHDESC</B>
<C>ZONEDESC</C>
…….
…….
</XLRow>
<XLRow>
<A>Quarter3 2004</A>
<B>August2004</B>
<C>North</C>
……
……
</XLRow>
…….
…….
</root >
The Spreadsheet ML document is first converted to a generic xml file format (above) using XSL transformation. This generic xml file can be transformed to an HTML table using XSLT and then styled with CSS for display in a web browser.
The XSLT used for HTML conversion is:
<?xml version="1.0" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="blnMakeHeaderFromElementName">true</xsl:param>
<xsl:param name="blnRetainFirstRowValAsHeader">true</xsl:param>
<xsl:template match="/">
<HTML>
<STYLE>
BODY {margin:0}
.rowEven {font:12pt Verdana; background-color:#FFFFCC;border-bottom:1px solid #CC88CC}
.rowOdd {font:12pt Verdana; border-bottom:1px; background-color: #e7e7e7 }
.header {font:bold 12pt Verdana;background-color:#C0C0C0; padding:2px; border:2px outset grey}
.rowHeader{font:bold 12pt Verdana;background-color:#C0C0C0; padding:2px; border:2px outset black}
</STYLE>
<script>
<![CDATA[
function Move(e) {
document.layers['Menu'].left=e.pageX
document.layers['Menu'].top=e.pageY
}
function Move2(){
document.all["Menu"].style.left=event.clientX + document.body.scrollLeft - document.body.clientLeft;
document.all["Menu"].style.top=event.clientY + document.body.scrollTop - document.body.clientTop;
document.all["Menu"].style.display = 'block';
}
function Follow() {
if (document.all) Move2()
}
function Loaded() {
if (document.layers) {
window.captureEvents(Event.MOUSEMOVE)
window.onmousemove=Move
}
}
function BacktoOrigin()
{
document.all["Menu"].style.left=0
document.all["Menu"].style.top=0
document.all["Menu"].style.display = 'none';
}
]]>
</script>
<BODY id="listing" onLoad="Loaded()" >
<DIV ID="Menu" NAME="Menu"
STYLE="position:absolute;top:10000;left:10000;background-color:#CCFFCC;padding:2px;">
<b>Edit</b>(Not-functional)
<br></br>
<b>Delete</b>(Not-functional)
</DIV>
<b>Guide Lines:</b>
<br></br>
<b>Click on "#" to see the table data(HTML) </b>
<i>Note: "View Source" will show the XML data (not HTML)</i>
<br></br>
<b>
Double click on any row to see it’s data(HTML)
</b>
<br></br>
<b>
Move Mouse over the <IMG SRC="image/ArrowHead.gif" WIDTH="9" HEIGHT="14"></IMG> image to view the popup menu(div).
</b>
<br></br>
<i>Currently the Menu(div) is Non-functional.</i>
<i>Menu's functionality (edit/delete) can be extended/implemented to meet the requirements of the user.Source code is downloadable </i>
<TABLE cellspacing="0" border="1" align="center" id="root">
<!--make the element name as header (A,B,C,D......)-->
<xsl:if test="not($blnMakeHeaderFromElementName='false')">
<TR>
<xsl:for-each select="root/XLRow[1]" >
<TD width="12" class="header" onClick="BLOCKED SCRIPTalert(document.getElementById('root').outerHTML);" >
#
</TD>
<xsl:for-each select="./*" >
<TD class="header">
<!--<xsl:attribute name="onClick">
sort('<xsl:value-of select="name()"/>')
</xsl:attribute>-->
<xsl:attribute name="name" >
<xsl:value-of select="name()"/>
</xsl:attribute>
<xsl:attribute name="id">
<xsl:value-of select="concat(name(),string(position()))"/>
</xsl:attribute>
<xsl:value-of select="name()"/>
</TD>
</xsl:for-each>
</xsl:for-each>
</TR>
</xsl:if>
<!--make the First Row -cell Values as header -->
<xsl:if test="not($blnRetainFirstRowValAsHeader='false')">
<TR >
<xsl:for-each select="root/XLRow[1]" >
<TD width="12" class="rowHeader" onClick="BLOCKED SCRIPTalert('Implement Addition of New Rows to table -Onclick of + ');">
+
</TD>
<xsl:for-each select="./*" >
<TD class="rowHeader">
<xsl:attribute name="name">
<xsl:value-of select="name()"/>
</xsl:attribute>
<xsl:attribute name="id">
<xsl:value-of select="concat(name(),string(position()))"/>
</xsl:attribute>
<xsl:value-of select="."/>
</TD>
</xsl:for-each>
</xsl:for-each>
</TR>
</xsl:if>
<xsl:for-each select="root/XLRow" >
<xsl:choose>
<xsl:when test="(not($blnRetainFirstRowValAsHeader='false') and position()=1)">
<!--Do nothing & skip this row,because this row is already made as header-->
</xsl:when>
<xsl:otherwise>
<TR ondblClick="BLOCKED SCRIPTalert(this.outerHTML)" onClick="BLOCKED SCRIPTthis.style.backgroundColor='CCFF33';">
<xsl:attribute name="name">
<xsl:value-of select="string('XLRow')"/>
</xsl:attribute>
<xsl:attribute name="id" >
<xsl:value-of select="concat('TR',string(position()))"/>
</xsl:attribute>
<xsl:choose>
<xsl:when test="position() mod 2 = 0">
<xsl:attribute name="class">
<xsl:value-of select="string('rowEven')"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="class">
<xsl:value-of select="string('rowOdd')"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<TD width="12">
<IMG SRC="image/ArrowHead.gif" WIDTH="9" HEIGHT="14" onMouseOver="BLOCKED SCRIPTFollow();" onMouseOut="BLOCKED SCRIPTBacktoOrigin();" >
<xsl:attribute name="id">
<xsl:value-of select="concat('IMG',string(position()))"></xsl:value-of>
</xsl:attribute>
</IMG>
<xsl:value-of select="position()"/>
</TD>
<xsl:for-each select="./*" >
<TD >
<xsl:attribute name="name">
<xsl:value-of select="name()"/>
</xsl:attribute>
<xsl:attribute name="id">
<xsl:value-of select="concat(name(),string(position()))"/>
</xsl:attribute>
<xsl:value-of select="."/>
</TD>
</xsl:for-each>
</TR>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</TABLE>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>
Parameters for XSLT
There are two XSLT parameters that give us control over the display of HTML Table headers.
<xsl:param name="blnMakeHeaderFromElementName"> </xsl:param>
<xsl:param name="blnRetainFirstRowValAsHeader"> </xsl:param>
|
Parameters |
Description |
Optional Values |
|
blnMakeHeaderFromElementName |
Make the Element names of the source XML file as headers |
true/false |
|
blnRetainFirstRowValAsHeader |
Make the First XML Node’s value as Header. |
true/false |
Let's analyze the effect of these parameters on the HTML output.
Case 1
When
|
Parameter |
Value |
|
blnMakeHeaderFromElementName |
False |
|
blnRetainFirstRowValAsHeader |
False |
Spreadsheet ML data displayed in HTML table looks like this:
Fig: XML2HTML_GenView.PNG

Case 2
When
|
Parameter |
Value |
|
blnMakeHeaderFromElementName |
True |
|
blnRetainFirstRowValAsHeader |
False |
Spreadsheet ML data displayed in HTML table looks like this:
Fig: XML2HTML_GenView_XCelHeader.PNG

Case 3
When
|
Parameter |
Value |
|
blnMakeHeaderFromElementName |
True |
|
blnRetainFirstRowValAsHeader |
True |
Spreadsheet ML data displayed in HTML table looks like this:
Fig: XML2HTML_GenView_FirstRowHeader.PNG

Extending this solution to implement additional functionality:
The XSLT (HTML table) also provides directives for addition and edit/delete of table data, thus making it more robust.
Note:
These are directives and not actual implementation
These directives aid the user to fore-see the various possibilities of making the html table more robust.
The objective of these directives are to kindle the creativity of the reader/developer in implementing “Edit/Delete” functionalities using the Row data (HTML).
The implementation of these functionalities is left to the requirement of the reader/developer.
Directives for Edit/Del of Row Data:
Move Mouse over the image (arrow head) to view the popup menu (div).
Double Click on any row to see it's Row data (HTML).
HTML table with Edit/Delete Menu:
Fig: XML2HTML_EditDelete.PNG

Directives for display of Row Data:
Onclick of row, highlight it.
Ondoubleclick of row, highlight it and show the Row data (HTML).
Fig: XML2HTML_HighlightRowWithData.png

Directives for Addition of Row Data:
Onclick of “+”, implement your own functionality to add new rows to the html table.
Fig: XML2HTML_NewRow.png

Thus the robustness of the HTML table generated using the XSLT is left to the imagination of the reader.
Hope this helps.
Authoring this article leaves me more inclined to believe that the ASP.NET server controls like DataGrid etc should have used similar kind of server-side XSLT(more robust) for HTML generation.
This pops up another thought -- “Paging” of HTML table! Can we do paging for the html table generated? Can we page records without using any of Microsoft’s Server side controls (asp net) like datagrid etc.? Can we implement paging for heterogonous applications with ease? The answer is “YES, We Can”, but that is for the next article (Part 2).
Bye for now.