XSLT 1.0按@item首字母分组时Item求和异常,求修正方案
XSLT 1.0按Item首字母分组并统计单个Item的Area总和修正方案
使用XSLT 1.0处理XML文件时,需要按@item属性的首字母分组展示,同时统计每个独立Item对应的area总和。当前实现中,所有首字母相同的Item的area会被错误累加(如a1和a2的area总和均显示为4),但预期应为每个Item单独求和(a1为2,a2为2;b1为2,b2为1)。
相关代码
XML文件
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="text/xsl" href="test_1.xsl"?> <Features> <Feature item="a1" area="1"></Feature> <Feature item="a1" area="1"></Feature> <Feature item="a2" area="1"></Feature> <Feature item="a2" area="1"></Feature> <Feature item="b1" area="1"></Feature> <Feature item="b1" area="1"></Feature> <Feature item="b2" area="1"></Feature> </Features>
XSLT文件
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:key name="KeyStyle" match="Feature" use="substring(@item,1,1)"/> <xsl:key name="KeyItem" match="Feature" use="@item"/> <xsl:template match="Features"> <html> <xsl:for-each select="Feature[generate-id() = generate-id(key('KeyStyle',substring(@item,1,1))[1])]"> <xsl:sort select="substring(@item,1,1)" order="ascending"/> Table for style (<xsl:value-of select="substring(@item,1,1)"/>) <xsl:for-each select="key('KeyStyle',substring(@item,1,1))"> <xsl:sort select="@item" order="ascending"/> <xsl:if test="not(preceding::*[@item = current()/@item])"> <p>Style=<xsl:value-of select="substring(@item,1,1)"/></p> <p>Item=<xsl:value-of select="@item"/></p> <p>TotalAreaOf_Items (<xsl:value-of select="@item"/>)=<xsl:value-of select="sum(key('KeyItem',@item)/@area)"/></p> </xsl:if> </xsl:for-each> ---------- <br/><br/> </xsl:for-each> </html> </xsl:template> </xsl:stylesheet>
当前错误输出
<html> Table for style (a) <p>Item=a1</p> <p>TotalAreaOf_Items (a1)=4</p> <p>Item=a2</p> <p>TotalAreaOf_Items (a2)=4</p> ----------------- Table for style (b) <p>Item=b1</p> <p>TotalAreaOf_Items (b1)=3</p> <p>Item=b2</p> <p>TotalAreaOf_Items (b2)=3</p> </html>
预期输出
<html> Table for style (a) <p>Style=a</p> <p>Item=a1</p> <p>TotalAreaOf_Items (a1)=2</p> <p>Style=a</p> <p>Item=a2</p> <p>TotalAreaOf_Items (a2)=2</p> ----------------- Table for style (b) <p>Style=b</p> <p>Item=b1</p> <p>TotalAreaOf_Items (b1)=2</p> <p>Style=b</p> <p>Item=b2</p> <p>TotalAreaOf_Items (b2)=1</p> </html>
修正方案
问题出在内部xsl:for-each中使用preceding::*[@item = current()/@item]的判断逻辑——这个轴会扫描整个文档中之前的所有节点,而非当前首字母分组内的节点,导致求和上下文出错。正确做法是在首字母分组内,再次按@item进行分组,用generate-id()获取每个Item的唯一实例。
修改后的XSLT代码如下:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:key name="KeyStyle" match="Feature" use="substring(@item,1,1)"/> <xsl:key name="KeyItem" match="Feature" use="@item"/> <xsl:template match="Features"> <html> <!-- 按首字母分组 --> <xsl:for-each select="Feature[generate-id() = generate-id(key('KeyStyle',substring(@item,1,1))[1])]"> <xsl:sort select="substring(@item,1,1)" order="ascending"/> Table for style (<xsl:value-of select="substring(@item,1,1)"/>) <!-- 存储当前首字母分组下的所有Feature节点 --> <xsl:variable name="currentStyleFeatures" select="key('KeyStyle',substring(@item,1,1))"/> <!-- 在当前分组内按@item二次分组 --> <xsl:for-each select="$currentStyleFeatures[generate-id() = generate-id(key('KeyItem',@item)[1])]"> <xsl:sort select="@item" order="ascending"/> <p>Style=<xsl:value-of select="substring(@item,1,1)"/></p> <p>Item=<xsl:value-of select="@item"/></p> <p>TotalAreaOf_Items (<xsl:value-of select="@item"/>)=<xsl:value-of select="sum(key('KeyItem',@item)/@area)"/></p> </xsl:for-each> ---------- <br/><br/> </xsl:for-each> </html> </xsl:template> </xsl:stylesheet>
修正说明
- 新增变量
currentStyleFeatures限定后续分组的范围,仅处理当前首字母分组内的节点。 - 内部循环改用
generate-id()判断每个@item的唯一实例,替代preceding::*的全局扫描,避免跨分组的节点干扰。 - 修正后
sum(key('KeyItem',@item)/@area)可准确计算每个独立Item的area总和,不会出现首字母相同的Item累加错误。
内容的提问来源于stack exchange,提问作者kapopoulos john
相关产品推荐
相关产品推荐

