XSLT中TripOverview内DropItem分组异常求助
XSLT分组问题:在独立TripOverview内分组DropItem元素
问题背景
需要对XML中每个TripOverview内部的DropItem元素进行分组,但当前XSLT实现未按预期生成分组结果,怀疑是key的层级设置有误。
输入XML示例
<MultiTripOverview> <TripOverview> <HeaderDetails> <DropItems> <DropItem> <GroupId>G1</GroupId> <ItemName>Item1</ItemName> </DropItem> <DropItem> <GroupId>G1</GroupId> <ItemName>Item2</ItemName> </DropItem> <DropItem> <GroupId>G2</GroupId> <ItemName>Item3</ItemName> </DropItem> </DropItems> </HeaderDetails> </TripOverview> <TripOverview> <HeaderDetails> <DropItems> <DropItem> <GroupId>G1</GroupId> <ItemName>ItemA</ItemName> </DropItem> <DropItem> <GroupId>G2</GroupId> <ItemName>ItemB</ItemName> </DropItem> </DropItems> </HeaderDetails> </TripOverview> </MultiTripOverview>
当前XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="dropItemGroup" match="DropItem" use="GroupId"/> <xsl:template match="/MultiTripOverview"> <Output> <xsl:apply-templates select="TripOverview"/> </Output> </xsl:template> <xsl:template match="TripOverview"> <Trip> <xsl:for-each select="HeaderDetails/DropItems/DropItem[generate-id() = generate-id(key('dropItemGroup', GroupId)[1])]"> <Group groupId="{GroupId}"> <xsl:for-each select="key('dropItemGroup', GroupId)"> <Item> <xsl:value-of select="ItemName"/> </Item> </xsl:for-each> </Group> </xsl:for-each> </Trip> </xsl:template> </xsl:stylesheet>
当前输出(不符合预期)
<Output> <Trip> <Group groupId="G1"> <Item>Item1</Item> <Item>Item2</Item> <Item>ItemA</Item> <!-- 错误:来自第二个TripOverview的元素被混入 --> </Group> <Group groupId="G2"> <Item>Item3</Item> <Item>ItemB</Item> <!-- 错误:来自第二个TripOverview的元素被混入 --> </Group> </Trip> <Trip> <!-- 没有生成分组,因为key已经匹配过所有同GroupId的元素 --> </Trip> </Output>
期望输出
<Output> <Trip> <Group groupId="G1"> <Item>Item1</Item> <Item>Item2</Item> </Group> <Group groupId="G2"> <Item>Item3</Item> </Group> </Trip> <Trip> <Group groupId="G1"> <Item>ItemA</Item> </Group> <Group groupId="G2"> <Item>ItemB</Item> </Group> </Trip> </Output>
问题排查与修正方案
核心问题
当前定义的dropItemGroup key仅以GroupId为分组依据,没有限定在当前TripOverview的上下文内,导致所有TripOverview中的同GroupId元素被归为一组,跨TripOverview的元素被错误混入。
修正步骤
- 修改key的
use属性:将分组依据改为当前TripOverview的唯一标识 +GroupId,确保分组范围限定在单个TripOverview内部。 - 同步修改for-each中的key调用:使用相同的组合键进行匹配。
修正后的XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 用TripOverview的generate-id + GroupId作为组合键,限定分组范围 --> <xsl:key name="dropItemGroup" match="DropItem" use="concat(generate-id(ancestor::TripOverview), '|', GroupId)"/> <xsl:template match="/MultiTripOverview"> <Output> <xsl:apply-templates select="TripOverview"/> </Output> </xsl:template> <xsl:template match="TripOverview"> <Trip> <xsl:variable name="currentTripId" select="generate-id()"/> <!-- 筛选当前TripOverview内的首个同组DropItem --> <xsl:for-each select="HeaderDetails/DropItems/DropItem[generate-id() = generate-id(key('dropItemGroup', concat($currentTripId, '|', GroupId))[1])]"> <Group groupId="{GroupId}"> <!-- 仅匹配当前TripOverview内的同组DropItem --> <xsl:for-each select="key('dropItemGroup', concat($currentTripId, '|', GroupId))"> <Item> <xsl:value-of select="ItemName"/> </Item> </xsl:for-each> </Group> </xsl:for-each> </Trip> </xsl:template> </xsl:stylesheet>
说明
- 组合键
concat(generate-id(ancestor::TripOverview), '|', GroupId)保证了每个TripOverview内的DropItem分组独立,不会跨TripOverview干扰。 - 定义
currentTripId变量复用当前TripOverview的唯一标识,提升代码可读性。
内容的提问来源于stack exchange,提问作者Mike Dole
相关产品推荐
相关产品推荐

