You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XSLT key仅首次调用生效,后续失效问题求助

XSLT key功能仅首次调用正常,后续失效问题

我用XSLT的key功能查询唯一元素列表,首次调用正常,但后续调用失效。我是XSLT新手,求帮助!

我的XSL文件内容

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt">

  <!-- Global Parameters -->
  <xsl:param name="POSLINK" select="'Y'"/>

  <!-- Set output method to indicate a text file (not XML) -->
  <xsl:output encoding="utf-8" method="text" media-type="plain/text"/>

  <!-- NAXML-MovementReport template -->
  <xsl:template match="NAXML-MovementReport">
    <xsl:apply-templates select="FuelGradeMovement/FGMDetail"/>
  </xsl:template>

  <!-- Key for unique LoyaltyName -->
  <xsl:key name="nameDistinct" match="Extension/LoyaltyDiscounts/LoyaltyDiscount" use="LoyaltyName"/>

  <xsl:template match="FGMDetail">
    <xsl:if test="$POSLINK='Y'">
      <xsl:variable name="fuelGradeIDs" select="FuelGradeID"/>
      <xsl:if test="count($fuelGradeIDs)=1">
        <xsl:variable name="fuelGradeID" select="string($fuelGradeIDs[1])"/>
        <xsl:if test="$fuelGradeID != ''">
          <!-- stops on the line below after first time -->
          <xsl:for-each select="Extension/LoyaltyDiscounts/LoyaltyDiscount[generate-id() = generate-id(key('nameDistinct', LoyaltyName)[1])]">
            <xsl:variable name="loyaltyName" select="string(LoyaltyName)"/>
            <xsl:variable name="vAllowedSymbols" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'"/>
            <xsl:variable name="fixedName" select="normalize-space(translate($loyaltyName, translate($loyaltyName, $vAllowedSymbols, ' '), ' '))"/>
            <xsl:variable name="amounts" select="../LoyaltyDiscount[LoyaltyName=$loyaltyName]/DiscountAmount"/>
            <xsl:variable name="total" select="round(sum(../LoyaltyDiscount[LoyaltyName=$loyaltyName]/DiscountAmount) * 100) div 100"/>
            <xsl:text>"DID",0,,"DTot-LoyDiscByFuelProdAmt-</xsl:text>
            <xsl:value-of select="$fixedName"/>
            <xsl:text>-</xsl:text>
            <xsl:value-of select="$fuelGradeID"/>
            <xsl:text>",</xsl:text>
            <xsl:value-of select="$total"/>
            <xsl:text>,,</xsl:text>
            <xsl:text>&#xa;</xsl:text>
          </xsl:for-each>
        </xsl:if>
      </xsl:if>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

对应的XML文件内容

<?xml version="1.0" encoding="ISO-8859-1"?>
<NAXML-MovementReport version="3.4">
    <TransmissionHeader>
        <StoreLocationID>2</StoreLocationID>
        <VendorName>Gilbarco-VeederRoot</VendorName>
        <VendorModelVersion>21.03.26.01</VendorModelVersion>
    </TransmissionHeader>
    <FuelGradeMovement>
        <MovementHeader>
            <ReportSequenceNumber>1</ReportSequenceNumber>
            <PrimaryReportPeriod>2</PrimaryReportPeriod>
            <SecondaryReportPeriod>0</SecondaryReportPeriod>
            <BusinessDate>2023-05-11</BusinessDate>
            <BeginDate>2023-05-11</BeginDate>
            <BeginTime>23:45:39</BeginTime>
            <EndDate>2023-05-12</EndDate>
            <EndTime>23:45:47</EndTime>
        </MovementHeader>
        <FGMDetail>
            <FuelGradeID>096</FuelGradeID>
            <Extension>
                <LoyaltyDiscounts>
                    <LoyaltyDiscount>
                        <LoyaltyName>Chevron &amp; Safeway</LoyaltyName>
                        <DiscountAmount>4.44</DiscountAmount>
                    </LoyaltyDiscount>
                    <LoyaltyDiscount>
                        <LoyaltyName>PM Loyalty</LoyaltyName>
                        <DiscountAmount>0.6</DiscountAmount>
                    </LoyaltyDiscount>
                </LoyaltyDiscounts>
                    <LoyaltyDiscount>
                        <LoyaltyName>Chevron &amp; Safeway</LoyaltyName>
                        <DiscountAmount>10.00</DiscountAmount>
                    </LoyaltyDiscount>
            </Extension>
        </FGMDetail>
        <FGMDetail>
            <FuelGradeID>001</FuelGradeID>
            <Extension>
                <LoyaltyDiscounts>
                    <LoyaltyDiscount>
                        <LoyaltyName>Chevron &amp; Safeway</LoyaltyName>
                        <DiscountAmount>123.8</DiscountAmount>
                    </LoyaltyDiscount>
                    <LoyaltyDiscount>
                        <LoyaltyName>PM Loyalty</LoyaltyName>
                        <DiscountAmount>0.42</DiscountAmount>
                    </LoyaltyDiscount>
                </LoyaltyDiscounts>
            </Extension>
        </FGMDetail>
        <FGMDetail>
            <FuelGradeID>003</FuelGradeID>
            <Extension>
                <LoyaltyDiscounts>
                    <LoyaltyDiscount>
                        <LoyaltyName>Chevron &amp; Safeway</LoyaltyName>
                        <DiscountAmount>38.32</DiscountAmount>
                    </LoyaltyDiscount>
                    <LoyaltyDiscount>
                        <LoyaltyName>PM Loyalty</LoyaltyName>
                        <DiscountAmount>0.07</DiscountAmount>
                    </LoyaltyDiscount>
                </LoyaltyDiscounts>
            </Extension>
        </FGMDetail>
        <FGMDetail>
            <FuelGradeID>019</FuelGradeID>
            <Extension>
                <LoyaltyDiscounts>
                    <LoyaltyDiscount>
                        <LoyaltyName>Chevron &amp; Safeway</LoyaltyName>
                        <DiscountAmount>12.63</DiscountAmount>
                    </LoyaltyDiscount>
                </LoyaltyDiscounts>
            </Extension>
        </FGMDetail>
    </FuelGradeMovement>
</NAXML-MovementReport>

目标与问题

我的目标是生成文本文件,包含唯一记录,每条记录含FuelGradeID、清理特殊字符后的LoyaltyName,以及对应DiscountAmount的总和。目前仅第一个FGMDetail元素有输出,需要让所有FGMDetail都生成对应记录。

期望输出

"DID",0,,"DTot-LoyDiscByFuelProdAmt-Chevron Safeway-096",14.44,,
"DID",0,,"DTot-LoyDiscByFuelProdAmt-PM Loyalty-096",0.6,,
"DID",0,,"DTot-LoyDiscByFuelProdAmt-Chevron Safeway-001",123.8,,
"DID",0,,"DTot-LoyDiscByFuelProdAmt-PM Loyalty-001",0.42,,
"DID",0,,"DTot-LoyDiscByFuelProdAmt-Chevron Safeway-003",38.32,,
"DID",0,,"DTot-LoyDiscByFuelProdAmt-PM Loyalty-003",0.07,,
"DID",0,,"DTot-LoyDiscByFuelProdAmt-Chevron Safeway-019",12.63,,

问题原因与解决方案

问题原因

  1. 全局key会对整个文档的LoyaltyName去重,导致后续FGMDetail里的重复LoyaltyName被过滤,无法生成输出。
  2. 原XPathExtension/LoyaltyDiscounts/LoyaltyDiscount漏掉了第一个FGMDetail下直接在Extension层级的LoyaltyDiscount,导致总和计算错误。
  3. 求和时的XPath上下文错误,可能跨FGMDetail求和。

修改后的XSL代码

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt">

  <!-- Global Parameters -->
  <xsl:param name="POSLINK" select="'Y'"/>

  <!-- Set output method to indicate a text file (not XML) -->
  <xsl:output encoding="utf-8" method="text" media-type="plain/text"/>

  <!-- NAXML-MovementReport template -->
  <xsl:template match="NAXML-MovementReport">
    <xsl:apply-templates select="FuelGradeMovement/FGMDetail"/>
  </xsl:template>

  <xsl:template match="FGMDetail">
    <xsl:if test="$POSLINK='Y'">
      <xsl:variable name="fuelGradeIDs" select="FuelGradeID"/>
      <xsl:if test="count($fuelGradeIDs)=1">
        <xsl:variable name="fuelGradeID" select="string($fuelGradeIDs[1])"/>
        <xsl:if test="$fuelGradeID != ''">
          <!-- 针对当前FGMDetail下的所有LoyaltyDiscount进行内部去重 -->
          <xsl:for-each select=".//LoyaltyDiscount[generate-id() = generate-id(current()/.//LoyaltyDiscount[LoyaltyName=current()/LoyaltyName][1])]">
            <xsl:variable name="loyaltyName" select="string(LoyaltyName)"/>
            <xsl:variable name="vAllowedSymbols" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz '"/>
            <!-- 保留空格,避免名称被合并 -->
            <xsl:variable name="fixedName" select="normalize-space(translate($loyaltyName, translate($loyaltyName, $vAllowedSymbols, ''), ''))"/>
            <!-- 限定上下文为当前FGMDetail,确保只求和当前分组下的金额 -->
            <xsl:variable name="total" select="round(sum(current()/.//LoyaltyDiscount[LoyaltyName=$loyaltyName]/DiscountAmount) * 100) div 100"/>
            <xsl:text>"DID",0,,"DTot-LoyDiscByFuelProdAmt-</xsl:text>
            <xsl:value-of select="$fixedName"/>
            <xsl:text>-</xsl:text>
            <xsl:value-of select="$fuelGradeID"/>
            <xsl:text>",</xsl:text>
            <xsl:value-of select="$total"/>
            <xsl:text>,,&#xa;</xsl:text>
          </xsl:for-each>
        </xsl:if>
      </xsl:if>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

关键修改点

  1. 移除全局key:不需要对整个文档的LoyaltyName去重,改为针对每个FGMDetail内部去重。
  2. 修改去重逻辑:for-each的select表达式确保只取当前FGMDetail下每个LoyaltyName的第一个实例。
  3. 修正XPath范围:用.//LoyaltyDiscount捕获当前FGMDetail下所有层级的LoyaltyDiscount,避免漏掉额外记录。
  4. 限定求和上下文:求和时使用current()/.//LoyaltyDiscount确保只计算当前FGMDetail下的金额,不会跨分组求和。
  5. 优化名称清理:在允许字符中加入空格,保证名称格式符合预期。

内容的提问来源于stack exchange,提问作者Belmiris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:45:01