XSLT 1.0嵌套分组问题:基于Muenchian方法实现数据分组
解决XSLT 1.0按NR分组并按ITEM映射列的表格转换问题
需求与现有问题
需要将给定的XML数据通过XSLT 1.0转换为指定结构的HTML表格:按NR字段分组,每个分组对应一行;每行需按固定顺序展示ITEM-A、ITEM-C、ITEM-B的相关属性(ITEM值、颜色、材质),缺失则留空。现有代码已实现NR分组,但无法正确映射不同ITEM到对应列。
原始XML数据
<?xml version="1.0" encoding="UTF-8"?> <RESULTSET> <RES> <NR>1</NR> <TYPE >XXX </TYPE> <ITEM>A</ITEM> <COLOUR>blue</COLOUR> <MATERIAL>wood</MATERIAL> <COUNTER>1</COUNTER> </RES> <RES> <NR>2</NR> <TYPE >YYY </TYPE> <ITEM>A</ITEM> <COLOUR>red</COLOUR> <MATERIAL>plastic</MATERIAL> <COUNTER>1</COUNTER> </RES> <RES> <NR>2</NR> <TYPE >YYY </TYPE> <ITEM>C</ITEM> <COLOUR>pink</COLOUR> <MATERIAL>wood</MATERIAL> <COUNTER>3</COUNTER> </RES> <RES> <NR>3</NR> <TYPE >ZZZ </TYPE> <ITEM>C</ITEM> <COLOUR>yellow</COLOUR> <MATERIAL>metal</MATERIAL> <COUNTER>3</COUNTER> </RES> <RES> <NR>1</NR> <TYPE >XXX</TYPE> <ITEM>B</ITEM> <COLOUR>yellow</COLOUR> <MATERIAL>metal</MATERIAL> <COUNTER>2</COUNTER> </RES> </RESULTSET>
期望输出HTML表格
<div class="s-table-container"> <table class="s-table"> <thead> <tr> <th>NR</th> <th>TYPE</th> <th>ITEM-A</th> <th>COL-A</th> <th>MAT-A</th> <th>ITEM-C</th> <th>COL-C</th> <th>MAT-C</th> <th>ITEM-B</th> <th>COL-B</th> <th>MAT-B</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>XXX</td> <td>A</td> <td>blue</td> <td>wood</td> <td></td> <td></td> <td></td> <td>B</td> <td>yellow</td> <td>metal</td> </tr> <tr> <td>2</td> <td>YYY</td> <td>A</td> <td>red</td> <td>plastic</td> <td>C</td> <td>pink</td> <td>wood</td> <td></td> <td></td> <td></td> </tr> <tr> <td>3</td> <td>ZZZ</td> <td></td> <td></td> <td></td> <td>C</td> <td>yellow</td> <td>metal</td> <td></td> <td></td> <td></td> </tr> </tbody> </table> </div>
现有XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="res-by-nr" match="RES" use="NR" /> <xsl:template match="RESULTSET"> <th>NR</th> <th>TYPE</th> <th>ITEM-A</th> <th>COL-A</th> <th>MAT-A</th> <th>ITEM-B</th> <th>COL-B</th> <th>MAT-B</th> <th>ITEM-C</th> <th>COL-C</th> <th>MAT-C</th> <br></br> <xsl:for-each select="RES[count(. | key('res-by-nr', NR)[1]) = 1]"> <xsl:sort select="NR" /> <tr> <td><xsl:value-of select="NR" /></td> <td><xsl:value-of select="TYPE" /></td> <xsl:for-each select="key('res-by-nr', NR)"> <xsl:sort select="Counter" /> <xsl:choose> <xsl:when test = "ITEM='A'"> <td><xsl:value-of select="ITEM"/></td> <td><xsl:value-of select="COLOUR"/></td> <td><xsl:value-of select="MATERIAL"/></td> </xsl:when> <xsl:otherwise> <td>Test</td> <td>Test</td> <td>Test</td> </xsl:otherwise> </xsl:choose> </xsl:for-each> </tr> </xsl:for-each> </xsl:template> </xsl:stylesheet>
修正后的XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- Muenchian分组:按NR分组 --> <xsl:key name="res-by-nr" match="RES" use="NR"/> <xsl:template match="/RESULTSET"> <div class="s-table-container"> <table class="s-table"> <thead> <tr> <th>NR</th> <th>TYPE</th> <th>ITEM-A</th> <th>COL-A</th> <th>MAT-A</th> <th>ITEM-C</th> <th>COL-C</th> <th>MAT-C</th> <th>ITEM-B</th> <th>COL-B</th> <th>MAT-B</th> </tr> </thead> <tbody> <!-- 遍历每个唯一的NR分组 --> <xsl:for-each select="RES[count(. | key('res-by-nr', NR)[1]) = 1]"> <xsl:sort select="NR" data-type="number"/> <tr> <!-- 输出NR和去空格后的TYPE --> <td><xsl:value-of select="NR"/></td> <td><xsl:value-of select="normalize-space(TYPE)"/></td> <!-- 处理ITEM-A的列 --> <xsl:variable name="itemA" select="key('res-by-nr', NR)[ITEM='A']"/> <xsl:choose> <xsl:when test="$itemA"> <td><xsl:value-of select="$itemA/ITEM"/></td> <td><xsl:value-of select="$itemA/COLOUR"/></td> <td><xsl:value-of select="$itemA/MATERIAL"/></td> </xsl:when> <xsl:otherwise> <td></td> <td></td> <td></td> </xsl:otherwise> </xsl:choose> <!-- 处理ITEM-C的列 --> <xsl:variable name="itemC" select="key('res-by-nr', NR)[ITEM='C']"/> <xsl:choose> <xsl:when test="$itemC"> <td><xsl:value-of select="$itemC/ITEM"/></td> <td><xsl:value-of select="$itemC/COLOUR"/></td> <td><xsl:value-of select="$itemC/MATERIAL"/></td> </xsl:when> <xsl:otherwise> <td></td> <td></td> <td></td> </xsl:otherwise> </xsl:choose> <!-- 处理ITEM-B的列 --> <xsl:variable name="itemB" select="key('res-by-nr', NR)[ITEM='B']"/> <xsl:choose> <xsl:when test="$itemB"> <td><xsl:value-of select="$itemB/ITEM"/></td> <td><xsl:value-of select="$itemB/COLOUR"/></td> <td><xsl:value-of select="$itemB/MATERIAL"/></td> </xsl:when> <xsl:otherwise> <td></td> <td></td> <td></td> </xsl:otherwise> </xsl:choose> </tr> </xsl:for-each> </tbody> </table> </div> </xsl:template> </xsl:stylesheet>
关键改进点
- 完善表格结构:补充了
thead、tbody以及外层的div容器,完全匹配期望输出的HTML结构。 - 固定列映射:不再遍历分组内的所有RES节点,而是直接按期望的列顺序(A→C→B)查找对应ITEM的节点,确保列位置正确。
- 处理空值:对每个ITEM类型,判断是否存在对应节点,不存在则输出空
<td>。 - 数据格式化:用
normalize-space(TYPE)去除TYPE字段的多余空格,和期望输出一致。 - 排序优化:给NR排序加上
data-type="number",确保数字排序正确。
内容的提问来源于stack exchange,提问作者Leonfauko
相关产品推荐
相关产品推荐

