最近碰到一个比较bt的xslt的转换需求,简化后,原xml是:
Code
<?xml version="1.0" encoding="utf-8" ?>
<root>
<x>x1</x>
<y>y1</y>
<x>x2</x>
<y>y2</y>
<z>z2</z>
<x>x3</x>
<y>y3</y>
<x>x4</x>
<z>z4</z>
<x>x5</x>
<y>y5</y>
</root>
<?xml version="1.0" encoding="utf-8" ?>
<root>
<x>x1</x>
<y>y1</y>
<x>x2</x>
<y>y2</y>
<z>z2</z>
<x>x3</x>
<y>y3</y>
<x>x4</x>
<z>z4</z>
<x>x5</x>
<y>y5</y>
</root>
而期望获得的html是一张表格:
Code
<table>
<tr>
<td>x</td>
<td>y</td>
<td>z</td>
</tr>
<tr>
<td>x1</td>
<td>y1</td>
<td></td>
</tr>
<tr>
<td>x2</td>
<td>y2</td>
<td>z2</td>
</tr>
<tr>
<td>x3</td>
<td>y3</td>
<td></td>
</tr>
<tr>
<td>x4</td>
<td></td>
<td>z4</td>
</tr>
<tr>
<td>x5</td>
<td>y5</td>
<td></td>
</tr>
</table>
<table>
<tr>
<td>x</td>
<td>y</td>
<td>z</td>
</tr>
<tr>
<td>x1</td>
<td>y1</td>
<td></td>
</tr>
<tr>
<td>x2</td>
<td>y2</td>
<td>z2</td>
</tr>
<tr>
<td>x3</td>
<td>y3</td>
<td></td>
</tr>
<tr>
<td>x4</td>
<td></td>
<td>z4</td>
</tr>
<tr>
<td>x5</td>
<td>y5</td>
<td></td>
</tr>
</table>
这里出现2个难题,一个是列名的distinct问题,另一个是如何正确的得到表格,而且还只能用xslt 1.0。经过整整一天的尝试,终于用写出来了一个xslt能够基本完成功能:
Code
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/root">
<html>
<title>test</title>
<body>
<xsl:variable name="dictinctNames">
<xsl:element name="items">
<xsl:call-template name="distinct"/>
</xsl:element>
</xsl:variable>
<table>
<tr>
<xsl:for-each select="msxsl:node-set($dictinctNames)/items/item">
<td>
<xsl:value-of select="@name"/>
</td>
</xsl:for-each>
</tr>
<xsl:call-template name="PrintValue">
<xsl:with-param name="items" select="*"/>
<xsl:with-param name="distinctItems" select="msxsl:node-set($dictinctNames)/items/item"/>
</xsl:call-template>
</table>
</body>
</html>
</xsl:template>
<!-- Distinct value and create elements -->
<xsl:template name="distinct">
<xsl:for-each select="*[generate-id(.) =generate-id(key('nameKey', name())[1])]">
<xsl:element name="item">
<xsl:attribute name="name">
<xsl:value-of select="name()"/>
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:template>
<!-- Set distinct key -->
<xsl:key name="nameKey" match="*" use="name()" />
<!-- Print table content-->
<xsl:template name="PrintValue">
<xsl:param name="items"/>
<xsl:param name="distinctItems"/>
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$items"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$distinctItems"/>
</xsl:call-template>
</xsl:template>
<!-- Core method -->
<xsl:template name="PrintValueCore">
<xsl:param name="nextItems"/>
<xsl:param name="distinctItems"/>
<xsl:param name="nextDistinctItems"/>
<!-- if (nextItems.Count>0) -->
<xsl:if test="count($nextItems)>0">
<xsl:if test="count($nextDistinctItems)=count($distinctItems)">
<xsl:text disable-output-escaping="yes"><![CDATA[<tr>]]></xsl:text>
</xsl:if>
<!-- if (nextDistinctItems.Count==0) -->
<xsl:if test="count($nextDistinctItems)=0">
<xsl:text disable-output-escaping="yes"><![CDATA[</tr>]]></xsl:text>
<!-- if (nextItems.Count>1) -->
<xsl:if test="count($nextItems)>1">
<!-- PrintValueCore(nextItems,distinctItems,nextDistinctItems); -->
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$nextItems"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$distinctItems"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
<!-- if (nextDistinctItems.Count>0) -->
<xsl:if test="count($nextDistinctItems)>0">
<!-- string first = nextDistinctItems[0].name -->
<xsl:variable name="first" select="$nextDistinctItems[1]/@name"/>
<!-- if (first==nextItem[0].name) -->
<xsl:if test="$first=name($nextItems)">
<td>
<!-- nextItem[0].value -->
<xsl:value-of select="$nextItems[1]"/>
</td>
<!-- PrintValueCore(nextItems.Skip(1),distinctItems,nextDistinctItems.Skip(1)); -->
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$nextItems/following::*"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$nextDistinctItems/following::*"/>
</xsl:call-template>
</xsl:if>
<!-- if (first!=nextItem[0].name) -->
<xsl:if test="not($first=name($nextItems))">
<td/>
<!-- PrintValueCore(nextItems,distinctItems,nextDistinctItems.Skip(1)); -->
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$nextItems"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$nextDistinctItems/following::*"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:if>
<!-- if (nextItems.Count==0) -->
<xsl:if test="count($nextItems)=0">
<!-- if (nextDistinctItems.Count>0) -->
<xsl:if test="count($nextDistinctItems)>0">
<td/>
<!-- PrintValueCore(nextItems,distinctItems,nextDistinctItems.Skip(1)); -->
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$nextItems"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$nextDistinctItems/following::*"/>
</xsl:call-template>
</xsl:if>
<!-- if (nextDistinctItems.Count==0) -->
<xsl:if test="count($nextDistinctItems)=0">
<xsl:text disable-output-escaping="yes"><![CDATA[</tr>]]></xsl:text>
</xsl:if>
</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:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/root">
<html>
<title>test</title>
<body>
<xsl:variable name="dictinctNames">
<xsl:element name="items">
<xsl:call-template name="distinct"/>
</xsl:element>
</xsl:variable>
<table>
<tr>
<xsl:for-each select="msxsl:node-set($dictinctNames)/items/item">
<td>
<xsl:value-of select="@name"/>
</td>
</xsl:for-each>
</tr>
<xsl:call-template name="PrintValue">
<xsl:with-param name="items" select="*"/>
<xsl:with-param name="distinctItems" select="msxsl:node-set($dictinctNames)/items/item"/>
</xsl:call-template>
</table>
</body>
</html>
</xsl:template>
<!-- Distinct value and create elements -->
<xsl:template name="distinct">
<xsl:for-each select="*[generate-id(.) =generate-id(key('nameKey', name())[1])]">
<xsl:element name="item">
<xsl:attribute name="name">
<xsl:value-of select="name()"/>
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:template>
<!-- Set distinct key -->
<xsl:key name="nameKey" match="*" use="name()" />
<!-- Print table content-->
<xsl:template name="PrintValue">
<xsl:param name="items"/>
<xsl:param name="distinctItems"/>
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$items"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$distinctItems"/>
</xsl:call-template>
</xsl:template>
<!-- Core method -->
<xsl:template name="PrintValueCore">
<xsl:param name="nextItems"/>
<xsl:param name="distinctItems"/>
<xsl:param name="nextDistinctItems"/>
<!-- if (nextItems.Count>0) -->
<xsl:if test="count($nextItems)>0">
<xsl:if test="count($nextDistinctItems)=count($distinctItems)">
<xsl:text disable-output-escaping="yes"><![CDATA[<tr>]]></xsl:text>
</xsl:if>
<!-- if (nextDistinctItems.Count==0) -->
<xsl:if test="count($nextDistinctItems)=0">
<xsl:text disable-output-escaping="yes"><![CDATA[</tr>]]></xsl:text>
<!-- if (nextItems.Count>1) -->
<xsl:if test="count($nextItems)>1">
<!-- PrintValueCore(nextItems,distinctItems,nextDistinctItems); -->
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$nextItems"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$distinctItems"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
<!-- if (nextDistinctItems.Count>0) -->
<xsl:if test="count($nextDistinctItems)>0">
<!-- string first = nextDistinctItems[0].name -->
<xsl:variable name="first" select="$nextDistinctItems[1]/@name"/>
<!-- if (first==nextItem[0].name) -->
<xsl:if test="$first=name($nextItems)">
<td>
<!-- nextItem[0].value -->
<xsl:value-of select="$nextItems[1]"/>
</td>
<!-- PrintValueCore(nextItems.Skip(1),distinctItems,nextDistinctItems.Skip(1)); -->
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$nextItems/following::*"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$nextDistinctItems/following::*"/>
</xsl:call-template>
</xsl:if>
<!-- if (first!=nextItem[0].name) -->
<xsl:if test="not($first=name($nextItems))">
<td/>
<!-- PrintValueCore(nextItems,distinctItems,nextDistinctItems.Skip(1)); -->
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$nextItems"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$nextDistinctItems/following::*"/>
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:if>
<!-- if (nextItems.Count==0) -->
<xsl:if test="count($nextItems)=0">
<!-- if (nextDistinctItems.Count>0) -->
<xsl:if test="count($nextDistinctItems)>0">
<td/>
<!-- PrintValueCore(nextItems,distinctItems,nextDistinctItems.Skip(1)); -->
<xsl:call-template name="PrintValueCore">
<xsl:with-param name="nextItems" select="$nextItems"/>
<xsl:with-param name="distinctItems" select="$distinctItems"/>
<xsl:with-param name="nextDistinctItems" select="$nextDistinctItems/following::*"/>
</xsl:call-template>
</xsl:if>
<!-- if (nextDistinctItems.Count==0) -->
<xsl:if test="count($nextDistinctItems)=0">
<xsl:text disable-output-escaping="yes"><![CDATA[</tr>]]></xsl:text>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
但是目前还很难把目标格式从转换方法中分离出来,这个就有待以后再改良吧。