在another question之后,我正在寻找一个更通用的解决方案来解决按自定义顺序重新排列表格列的问题。我找不到任何东西,所以我把我的贴在这里。
输入 XML
<table>
<row>
<cell>NAME</cell>
<cell>DATE</cell>
<cell>SIZE</cell>
<cell>COLOR</cell>
</row>
<row>
<cell>Alpha</cell>
<cell>2023-02-19</cell>
<cell>Small</cell>
<cell>Red</cell>
</row>
<row>
<cell>Bravo</cell>
<cell>2023-02-19</cell>
<cell>Small</cell>
<cell>Green</cell>
</row>
<row>
<cell>Charlie</cell>
<cell>2023-02-19</cell>
<cell>Small</cell>
<cell>Blue</cell>
</row>
</table>
预期产出
<table border="1">
<tr>
<th>DATE</th>
<th>COLOR</th>
<th>SIZE</th>
<th>NAME</th>
</tr>
<tr>
<td>2023-02-19</td>
<td>Red</td>
<td>Small</td>
<td>Alpha</td>
</tr>
<tr>
<td>2023-02-19</td>
<td>Green</td>
<td>Small</td>
<td>Bravo</td>
</tr>
<tr>
<td>2023-02-19</td>
<td>Blue</td>
<td>Small</td>
<td>Charlie</td>
</tr>
</table>
XSLT 1.0(具有 EXSLT node-set() 扩展功能)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="new-order">DATE,COLOR,SIZE,NAME</xsl:param>
<xsl:template match="/table">
<xsl:variable name="columns-RTF">
<xsl:call-template name="generate-columns">
<xsl:with-param name="headings" select="$new-order"/>
<xsl:with-param name="top-row-cells" select="row[1]/cell"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="columns" select="exsl:node-set($columns-RTF)/column" />
<!-- output -->
<table border="1">
<!-- header -->
<tr>
<xsl:for-each select="$columns">
<th>
<xsl:value-of select="." />
</th>
</xsl:for-each>
</tr>
<!-- data -->
<xsl:for-each select="row[position() > 1]">
<tr>
<xsl:variable name="cells" select="cell" />
<xsl:for-each select="$columns">
<td>
<xsl:value-of select="$cells[position() = current()/@i]" />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="generate-columns">
<xsl:param name="headings"/>
<xsl:param name="top-row-cells"/>
<xsl:param name="delimiter" select="','"/>
<xsl:variable name="token" select="substring-before(concat($headings, $delimiter), $delimiter)" />
<column i="{count($top-row-cells[. = $token]/preceding-sibling::cell) + 1}">
<xsl:value-of select="$token"/>
</column>
<xsl:if test="contains($headings, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="generate-columns">
<xsl:with-param name="headings" select="substring-after($headings, $delimiter)"/>
<xsl:with-param name="top-row-cells" select="$top-row-cells"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
使用支持
tokenize()
扩展功能的处理器,这可以简化为:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="exsl str">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="new-order">DATE,COLOR,SIZE,NAME</xsl:param>
<xsl:template match="/table">
<xsl:variable name="top-row-cells" select="row[1]/cell"/>
<xsl:variable name="columns-RTF">
<xsl:for-each select="str:tokenize($new-order, ',')">
<column i="{count($top-row-cells[. = current()]/preceding-sibling::cell) + 1}">
<xsl:value-of select="."/>
</column>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="columns" select="exsl:node-set($columns-RTF)/column" />
<!-- output -->
<table border="1">
<!-- header -->
<tr>
<xsl:for-each select="$columns">
<th>
<xsl:value-of select="." />
</th>
</xsl:for-each>
</tr>
<!-- data -->
<xsl:for-each select="row[position() > 1]">
<tr>
<xsl:variable name="cells" select="cell" />
<xsl:for-each select="$columns">
<td>
<xsl:value-of select="$cells[position() = current()/@i]" />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>