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>
</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 & Safeway</LoyaltyName> <DiscountAmount>4.44</DiscountAmount> </LoyaltyDiscount> <LoyaltyDiscount> <LoyaltyName>PM Loyalty</LoyaltyName> <DiscountAmount>0.6</DiscountAmount> </LoyaltyDiscount> </LoyaltyDiscounts> <LoyaltyDiscount> <LoyaltyName>Chevron & Safeway</LoyaltyName> <DiscountAmount>10.00</DiscountAmount> </LoyaltyDiscount> </Extension> </FGMDetail> <FGMDetail> <FuelGradeID>001</FuelGradeID> <Extension> <LoyaltyDiscounts> <LoyaltyDiscount> <LoyaltyName>Chevron & 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 & 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 & 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,,
问题原因与解决方案
问题原因
- 全局key会对整个文档的LoyaltyName去重,导致后续FGMDetail里的重复LoyaltyName被过滤,无法生成输出。
- 原XPath
Extension/LoyaltyDiscounts/LoyaltyDiscount漏掉了第一个FGMDetail下直接在Extension层级的LoyaltyDiscount,导致总和计算错误。 - 求和时的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>,,
</xsl:text> </xsl:for-each> </xsl:if> </xsl:if> </xsl:if> </xsl:template> </xsl:stylesheet>
关键修改点
- 移除全局key:不需要对整个文档的LoyaltyName去重,改为针对每个FGMDetail内部去重。
- 修改去重逻辑:
for-each的select表达式确保只取当前FGMDetail下每个LoyaltyName的第一个实例。 - 修正XPath范围:用
.//LoyaltyDiscount捕获当前FGMDetail下所有层级的LoyaltyDiscount,避免漏掉额外记录。 - 限定求和上下文:求和时使用
current()/.//LoyaltyDiscount确保只计算当前FGMDetail下的金额,不会跨分组求和。 - 优化名称清理:在允许字符中加入空格,保证名称格式符合预期。
内容的提问来源于stack exchange,提问作者Belmiris
相关产品推荐
相关产品推荐

