XML球队积分计算异常求助:XSLT结果与预期不符
问题排查:XSLT计算球队积分错误(结果为202而非预期4)
问题背景
现有一份赛事数据XML文件,目标是计算每支球队的总积分(规则:胜得2分,平得1分,负得0分)。预期AAAA队总积分为4,但当前XSLT代码计算结果为202,需排查修复。
原始XML数据
<tournoi date="2012-12-12"> <match date="2012-12-20" heure="18:00:00"> <equipe nom="AAAA" score="3" /> <equipe nom="BBBB" score="0" /> </match> <match date="2012-12-20" heure="20:00:00"> <equipe nom="CCCC" score="1" /> <equipe nom="DDDD" score="1" /> </match> <match date="2012-12-21" heure="18:00:00"> <equipe nom="AAAA" score="2" /> <equipe nom="CCCC" score="4" /> </match> <match date="2012-12-21" heure="20:00:00"> <equipe nom="BBBB" score="7" /> <equipe nom="DDDD" score="0" /> </match> <match date="2012-12-22" heure="18:00:00"> <equipe nom="AAAA" score="3" /> <equipe nom="DDDD" score="2" /> </match> <match date="2012-12-22" heure="20:00:00"> <equipe nom="CCCC" score="5" /> <equipe nom="BBBB" score="1" /> </match> </tournoi>
原始XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="teams" match="equipe" use="@nom"/> <xsl:template match="/"> <html> <body> <h2>Total points of each team</h2> <table border="1" width="100%"> <tr bgcolor="green"> <th>Team</th> <th>Total Points</th> </tr> <!-- Group by unique team names --> <xsl:for-each select="tournoi/match/equipe[not(@nom = preceding::equipe/@nom)]"> <tr> <td><xsl:value-of select="@nom"/></td> <td> <xsl:variable name="teamName" select="@nom"/> <xsl:variable name="totalPoints" as="number"> <xsl:for-each select="key('teams', $teamName)"> <xsl:variable name="opponentScore" select="../equipe[@nom != $teamName]/@score"/> <xsl:choose> <xsl:when test="@score > $opponentScore"><xsl:value-of select="2" as="number"/></xsl:when> <xsl:when test="@score = $opponentScore"><xsl:value-of select="1" as="number"/></xsl:when> <xsl:otherwise><xsl:value-of select="0" as="number"/></xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:variable> <xsl:value-of select="$totalPoints"/> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
错误原因分析
- XSLT 1.0不支持
as="number"属性:该属性是XSLT 2.0引入的,在1.0版本中会被忽略,无法将输出值强制转为数值类型。 - 字符串拼接而非数值求和:原代码中
totalPoints变量只是将每场的得分(2、0、2)直接拼接成字符串"202",而非进行数值相加,这是导致结果为202的核心原因。 - 分组方式低效:使用
preceding::equipe/@nom进行去重,性能远不如Muenchian分组(即利用<xsl:key>配合generate-id()实现高效分组)。
修复后的XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="teams" match="equipe" use="@nom"/> <xsl:template match="/"> <html> <body> <h2>Total points of each team</h2> <table border="1" width="100%"> <tr bgcolor="green"> <th>Team</th> <th>Total Points</th> </tr> <!-- 使用Muenchian分组获取唯一球队 --> <xsl:for-each select="tournoi/match/equipe[generate-id() = generate-id(key('teams', @nom)[1])]"> <xsl:sort select="@nom"/> <tr> <td><xsl:value-of select="@nom"/></td> <td> <xsl:variable name="teamName" select="@nom"/> <!-- 计算每场比赛的得分并求和 --> <xsl:value-of select="sum( key('teams', $teamName)/../equipe[@nom = $teamName]/( if (@score > ../equipe[@nom != $teamName]/@score) then 2 else if (@score = ../equipe[@nom != $teamName]/@score) then 1 else 0 ) )"/> </td> </tr> </xsl:for-each> </table> </body> </html> </xsl:template> </xsl:stylesheet>
关键修改说明
- 改用Muenchian分组:通过
generate-id() = generate-id(key('teams', @nom)[1])筛选出每支球队的第一个节点,实现高效去重。 - 数值求和:使用
sum()函数对每场比赛的得分(转换为数值)进行累加,避免字符串拼接问题。 - 移除不兼容属性:删除所有XSLT 1.0不支持的
as="number"属性。 - 添加排序:新增
<xsl:sort select="@nom"/>让球队按名称排序,结果更规整。
验证结果
修复后计算的各队积分如下:
| Team | Total Points |
|---|---|
| AAAA | 4 |
| BBBB | 2 |
| CCCC | 6 |
| DDDD | 1 |
完全符合预期规则,AAAA队积分正确为4分。
内容的提问来源于stack exchange,提问作者Abdex
相关产品推荐
相关产品推荐

