请接受霍尔曼的解决方案。他的技术很到位。
XSLT 2.0 解决方案
您的示例输入数据建议使用分号结尾的列表,而不是分号分隔的列表。
此 XSLT 2.0 样式表可以与分号终止的列表或分号分隔的列表一起正常工作。它接受一个输入参数col-count
,默认为 3。
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:param name="col-count" select="3" />
<xsl:template match="/*">
<table border="1">
<xsl:variable name="names" select="tokenize(.,';')[normalize-space(.)]" />
<xsl:for-each-group select="$names , for $x in 1 to
($col-count - (count($names) mod $col-count)) mod $col-count return ''"
group-by="(position() - 1) idiv $col-count">
<tr>
<xsl:for-each select="current-group()">
<td><xsl:value-of select="normalize-space(.)" /></td>
</xsl:for-each>
</tr>
</xsl:for-each-group>
</table>
</xsl:template>
</xsl:stylesheet>
笔记:
(1) 注意解决方案的简洁性,并且没有笨拙的xsl:if
陈述。计算序列的使用(与输入文档节点选择相反)不仅限于 group-by 属性,还可以应用于select
. 计算序列的使用表明了更实用的视图,而不是程序视图。
(2) 如果您希望输出对于旧浏览器是安全的,那么替换...
<xsl:for-each-group select="$names , for $x in 1 to
($col-count - (count($names) mod $col-count)) mod $col-count return ''"
group-by="(position() - 1) idiv $col-count">
... 和 ...
<xsl:for-each-group select="$names , for $x in 1 to
($col-count - (count($names) mod $col-count)) mod $col-count return ' '"
group-by="(position() - 1) idiv $col-count">
XSLT 1.0 解决方案
如果您停留在 XSLT 1.0 上,那么您可以使用这个等效但效率较低的样式表......
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:esl="http://exslt.org/common"
xmlns:so="http://stackoverflow.com/questions/18066463"
version="1.0"
exclude-result-prefixes="xsl so esl">
<xsl:output method="html" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:variable name="col-count" select="3" /><!-- Must be 2 or 3. -->
<xsl:variable name="empty-rtf">
<so:name> </so:name>
</xsl:variable>
<xsl:variable name="empty" select="esl:node-set($empty-rtf)/*" />
<xsl:template match="/*">
<table border="1">
<xsl:variable name="names-rtf">
<xsl:call-template name="split">
<xsl:with-param name="list" select="." />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="names" select="esl:node-set($names-rtf)/*" />
<xsl:for-each select="$names[(position() mod $col-count) = 1]">
<xsl:variable name="row" select="position() - 1" />
<tr>
<xsl:apply-templates select="$names[floor((position() - 1) div $col-count) = $row]" />
<xsl:if test="position()=last()">
<xsl:for-each select="($names|$empty)[position() <=
(($col-count - (count($names) mod $col-count)) mod $col-count)]">
<xsl:apply-templates select="$empty" />
</xsl:for-each>
</xsl:if>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="split">
<xsl:param name="list" />
<xsl:choose>
<xsl:when test="contains($list,';')">
<xsl:call-template name="split">
<xsl:with-param name="list" select="substring-before($list,';')" />
</xsl:call-template>
<xsl:call-template name="split">
<xsl:with-param name="list" select="substring-after($list,';')" />
</xsl:call-template>
</xsl:when>
<xsl:when test="normalize-space($list)">
<so:name><xsl:value-of select="normalize-space($list)" /></so:name>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
<xsl:template match="so:*" priority="2">
<td><xsl:value-of select="." /></td>
</xsl:template>
</xsl:stylesheet>