基于XSLT 1.0单模板按属性合并节点并求和的优化方案
XSLT 1.0 优化分组求和实现,减少循环次数
需求:在XSLT 1.0中使用单个模板,生成包含唯一role的结构,每个role节点的totalQty属性为对应所有bar节点qty属性的总和,且每个role下包含对应的bar子节点。
我当前实现的代码存在4次循环(2次for-each循环、1次sum中的循环、1次key中的循环),想知道有没有更优方式减少循环次数。
输入XML
<foo> <bar qty="1" unit="123"> <var role="abc"/> </bar> <bar qty="1" unit="123"> <var role="efg"/> </bar> <bar qty="2" unit="501"> <var role="efg"/> </bar> <bar qty="1" unit="123"> <var role="efg"/> </bar> <bar qty="1" unit="123"> <var role="abc"/> </bar> <bar qty="1" unit="9085"> <var role="abc"/> </bar> </foo>
期望输出XML
<foo> <vars> <var role="abc" totalQty="3"> <bar qty="1" unit="123" /> <bar qty="1" unit="123" /> <bar qty="1" unit="9085" /> </var> <var role="efg" totalQty="4"> <bar qty="1" unit="123" /> <bar qty="2" unit="501" /> <bar qty="1" unit="123" /> </var> </vars> </foo>
现有XSLT 1.0代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:key name="uniquerole" match="//bar/var" use="@role"/> <xsl:template match="/"> <foo> <vars> <xsl:for-each select="//bar/var[generate-id() = generate-id(key('uniquerole', @role))]"> <xsl:variable name="vRole"> <xsl:value-of select="@role"/> </xsl:variable> <xsl:variable name="tot_qty"> <xsl:value-of select="sum(//bar[var/@role=$vRole]/@qty)"/> </xsl:variable> <var> <xsl:attribute name="role"> <xsl:value-of select="$vRole"/> </xsl:attribute> <xsl:attribute name="totalQty"> <xsl:value-of select="$tot_qty"/> </xsl:attribute> <xsl:for-each select="//bar[var/@role=$vRole]"> <bar> <xsl:attribute name="qty"> <xsl:value-of select="@qty"/> </xsl:attribute> <xsl:attribute name="unit"> <xsl:value-of select="@unit"/> </xsl:attribute> </bar> </xsl:for-each> </var> </xsl:for-each> </vars> </foo> </xsl:template> </xsl:stylesheet>
优化方案
核心思路是调整Muenchian分组的key定义,直接以bar节点为匹配对象,用var/@role作为键值,这样分组后可以直接复用同一role对应的所有bar节点集合,避免多次遍历整个文档查询:
- 重新定义key,直接匹配
bar节点,键值为var/@role,一次建立索引后后续所有操作复用该索引 - 分组时选择每个
role组的第一个bar节点作为分组标识 - 计算总和、生成子
bar节点时,直接调用key获取对应集合,无需重复查询
优化后的XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- 直接以bar为匹配对象,用var的role属性作为键 --> <xsl:key name="role-bar" match="bar" use="var/@role"/> <xsl:template match="/"> <foo> <vars> <!-- 按role分组,选择每个组的第一个bar作为分组入口 --> <xsl:for-each select="foo/bar[generate-id() = generate-id(key('role-bar', var/@role)[1])]"> <xsl:variable name="current-role" select="var/@role"/> <!-- 直接通过key获取当前role的所有bar节点集合 --> <xsl:variable name="role-bars" select="key('role-bar', $current-role)"/> <var role="{$current-role}" totalQty="{sum($role-bars/@qty)}"> <!-- 遍历复用已获取的bar集合,无需重新查询 --> <xsl:for-each select="$role-bars"> <bar qty="{@qty}" unit="{@unit}"/> </xsl:for-each> </var> </xsl:for-each> </vars> </foo> </xsl:template> </xsl:stylesheet>
优化说明
- 循环次数大幅减少:key仅在初始化时遍历一次文档建立索引;分组遍历一次唯一组;每个组内的sum和子节点生成复用同一个
role-bars集合,无需重复遍历文档查询 - 使用属性值模板(
{})简化属性生成代码,更简洁高效 - 避免了原代码中多次使用
//bar的全局查询,减少不必要的节点遍历
内容的提问来源于stack exchange,提问作者2FaceMan
相关产品推荐
相关产品推荐

