摘要:本文通過實際的例子來說明如何在XSL中實現對XML數據轉換成完美的多列表格。
在利用XSL對XML進行轉換時,有時候需要把XML轉換成多列的Table元素,這個問題經常會困擾許多人,如果不生成Table的話,只需要對循環中的節點進行位置取模後判斷,然後用<br/>換行即可。但有時候為了用戶需要和界面的美觀,需要生成多行多列的Table,常用的方法是采用following-sibling進行判斷,比如下面的代碼:
<?XML version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
XMLns:copyRight="http://xml.sz.luohuedu.net/">
<xsl:template match="/">
<table bgcolor="snow" border="1" cellpadding="5" cellspacing="2" borderColor="darkorange"
style="font-size:9pt">
<xsl:for-each select="/Items/Item[position() mod 3 = 1]">
<tr>
<td width="33%" align="center" valign="middle">
<xsl:apply-templates select="."/>
</td>
<td width="34%" align="center" valign="middle">
<xsl:apply-templates select="following-sibling::Item[position() = 1]"/>
</td>
<td width="33%" align="center" valign="middle">
<xsl:apply-templates select="following-sibling::Item[position() = 2]"/>
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="/Items/Item">
<a target="_blank">
<xsl:attribute name="href">
<xsl:if test="contains(Url,'@')">mailto:</xsl:if><xsl:value-of select="Url"/></xsl:attribute>
<xsl:value-of select="Title"/>
</a>
</xsl:template>
</xsl:stylesheet>
轉換結果如下圖所示:
但是,從上面的結果圖可以看出,如果數據記錄不是表格列數的整數倍的時候,最後一行的最後幾列不會生成出來,即在Html裡缺少<td></td>標記。
要解決上面的不足,我們必須先計算要轉換的記錄的總數,然後計算出相差的記錄數,再進行補齊。下面就對這一過程的解釋。
在進行這個功能之前,首先了解一個如何在XSL中實現類似for(i=0;i<n;i++)的循環,下面就是實現這一個功能的例子:
<?XML version="1.0" encoding="GB2312"?>
<xsl:stylesheet version="1.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
XMLns:copyRight="http://xml.sz.luohuedu.net/">
<!-- 下面的三個變量可以由XML中取得,做為例子,這裡直接定義了初始值 -->
<!-- 定義初始值 -->
<xsl:variable name="varStart" select="0"/>
<!-- 定義結束值 -->
<xsl:variable name="varEnd" select="35"/>
<!-- 定義循環步長 -->
<xsl:variable name="varStep" select="2"/>
<xsl:template match="/">
<xsl:call-template name="MyLoopFun">
<xsl:with-param name="varStart" select="$varStart">
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="MyLoopFun">
<xsl:param name="varStart"/>
<xsl:if test="$varStart < $varEnd">
<!-- 輸出格式定義 -->
<a target="_blank" href="http://xml.sz.luohuedu.net/?{$varStart}">
<xsl:attribute name="title"><xsl:value-of select="$varStart"/></xsl:attribute>
<xsl:value-of select="$varStart"/>
</a>
<xsl:if test="$varStart < ($varEnd - $varStep)"> , </xsl:if>
<xsl:call-template name="MyLoopFun">
<xsl:with-param name="varStart">
<xsl:value-of select="$varStart + $varStep"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
理解了上面的原理之後,下面就是我們最後的代碼,程序已經做了注釋:
<?XML version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" XMLns:xsl="http://www.w3.org/1999/XSL/Transform"
XMLns:copyRight="http://xml.sz.luohuedu.net/">
<xsl:template match="/">
<!-- 定義常量 -->
<xsl:variable name="strTrLeft" select="'<tr>'"/>
<xsl:variable name="strTrRight" select="'</tr>'"/>
<!-- 計算總記錄數 -->
<xsl:variable name="nTotal" select="count(/Items/Item)"/>
<!-- 定義列數 -->
<xsl:variable name="nCols" select="3"/>
<!-- 計算需要的補齊的列數 -->
<xsl:variable name="nLefted" select="$nCols - ($nTotal mod $nCols)"/>
<!-- 計算不需要補齊的行數 -->
<xsl:variable name="nNotProcessedRow" select="$nTotal - ($nTotal mod $nCols)"/>
<table bgcolor="snow" border="1" cellpadding="5" cellspacing="2" borderColor="darkorange"
style="font-size:9pt">
<!-- 對於不需要補齊的行數,直接輸出 -->
<xsl:for-each select="/Items/Item[position() < $nNotProcessedRow +1]">
<xsl:if test="position() mod $nCols = 1">
<xsl:value-of select="$strTrLeft" disable-output-escaping="yes"/>
</xsl:if>
<td>
<a target="_blank">
<xsl:attribute name="href">
<xsl:if test="contains(Url,'@')">mailto:</xsl:if><xsl:value-of select="Url"/></xsl:attribute>
<xsl:value-of select="Title"/>
</a>
</td>
<xsl:if test="position() mod $nCols = 0">
<xsl:value-of select="$strTrRight" disable-output-escaping="yes"/>
</xsl:if>
</xsl:for-each>
<!-- 轉換除去不需要補齊的記錄的剩余記錄 -->
<xsl:if test="$nLefted != 0 and $nLefted != $nCols">
<xsl:value-of select="$strTrLeft" disable-output-escaping="yes"/>
<xsl:for-each select="/Items/Item[position() >$nNotProcessedRow]">
<td>
<a target="_blank">
<xsl:attribute name="href">
<xsl:if test="contains(Url,'@')">mailto:</xsl:if><xsl:value-of select="Url"/></xsl:attribute>
<xsl:value-of select="Title"/>
</a>
</td>
</xsl:for-each>
<!--
如果nLefted不等於0和列數,則需要進行補齊,這裡進行遞歸調用,需要傳遞的參數有兩個:
nLefted:要補齊的列數;
nCols:表格的列數。
-->
<xsl:call-template name="MyFun">
<xsl:with-param name="nLefted" select="$nLefted"/>
<xsl:with-param name="nCols" select="$nCols"/>
</xsl:call-template>
<xsl:value-of select="$strTrRight" disable-output-escaping="yes"/>
</xsl:if>
</table>
<p>共有<xsl:value-of select="$nTotal"/>條數據。</p>
</xsl:template>
<xsl:template name="MyFun">
<xsl:param name="nLefted"/>
<xsl:param name="nCols"/>
<xsl:if test=" $nLefted != 0 and $nLefted != $nCols">
<td>
<xsl:text disable-output-escaping="yes">&nbsp;</xsl:text>
</td>
<xsl:call-template name="MyFun">
<xsl:with-param name="nLefted" select="$nLefted - 1"/>
<xsl:with-param name="nCols" select="$nCols"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
由於http://www.w3.org/1999/XSL/Transform名稱控件只有IE5.5+才支持,為了使我們的代碼具有通用性,我們在服務器端進行轉換,首先建立GoodLoop.ASPx如下:
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="GoodLoop.ASPx.vb"
Inherits="ASPxWeb.mengxianhui.com.GoodLoop"%>
<%@ Import NameSpace = "System" %>
<%@ Import NameSpace = "System.XML" %>
<%@ Import NameSpace = "system.XML.Xsl" %>
<!doctype HTML PUBLIC "-//W3C//DTD Html 4.0 Transitional//EN">
<Html>
<head>
<title>GoodLoop</title>
<meta content="Microsoft Visual Studio .Net 7.0" name="GENERATOR">
<meta content="Visual Basic 7.0" name="CODE_LANGUAGE">
<meta content="JavaScript" name="vs_defaultClIEntScript">
<meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
<script runat=Server>
Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs)
Try
XMLDocument()
Xmldom.Load(Server.MapPath("GoodLoop.XML"))
Dim trans As XslTransform = New XslTransform()
trans.Load(Server.MapPath("GoodLoop.xsl"))
Xml1.Document = XMLdom
XML1.Transform = trans
Catch er As XMLException
Label1.Text = er.Message
End Try
End Sub
</script>
</head>
<body MS_POSITIONING="GridLayout">
<form id="Form1" method="post" runat="server">
<asp:label id="Label1" runat="server"></ASP:label>
<asp:xml id="XML1" runat="server"></ASP:XML>
</form>
</body>
</Html>
本文中所使用的XML數據樣式為:GoodLoop.XML
<?XML version="1.0" encoding="UTF-8"?>
<?XML-stylesheet type="text/xsl" href="GoodLoop1.xsl"?>
<items>
<item>
<url>http://XML.sz.luohuedu.Net</url>
<title>【孟憲會之精彩世界】</title>
</item>
<item>
<url>http://lucky.myrice.com/</url>
<title>【孟憲會之精彩世界】</title>
</item>
...............................
</items>
轉換結果如下: