如何修改XSLT 1.0代码,实现相同ID元素合并并提取组内非空值?
问题:XSLT 1.0合并同ID元素并提取非空值及独有内容
需要用XSLT 1.0转换XML,实现合并相同ID的元素,提取同ID组内的非空值以及组中某元素独有的内容。当前编写的转换代码仅在同ID组的第一个元素包含所有非空值时生效。
示例中,第一个<payment>的<Amount>为空,但同ID组的第二个<payment>包含该元素;<Estimate>元素则相反;<Option>仅存在于同ID组的第一个<payment>中。
源XML
<?xml version="1.0" encoding="utf-8"?> <Data> <payment Name="John Curtis"> <Account Type="Personal" Id="111" Token="aaa"/> <Amount/> <Estimate Val="30"/> <Option Val="Yes"/> </payment> <payment Name="John Curtis"> <Account Type="Personal" Id="111" Token="aaa"/> <Amount Paid="10"> <Breakout> <Misc Desc="Interest" Amount="8"/> <Misc Desc="Principal" Amount="2"/> </Breakout> </Amount> <Estimate/> </payment> <payment Name="Elvis Kans"> <Account Type="Personal" Id="222" Token="bbb"/> <Amount Paid="5"> <Breakout> <Misc Desc="Interest" Amount="3"/> <Misc Desc="Principal" Amount="2"/> </Breakout> </Amount> <Estimate Val="10"/> <Option Val="Yes"/> </payment> </Data>
预期输出
<?xml version="1.0" encoding="UTF-8"?> <output> <result> <item VAL="Name">John Curtis</item> <group> <header VAL="Total">10</header> <item VAL="Item_1">8</item> <item VAL="Item_2">2</item> </group> <item VAL="Estimate">30</item> <item VAL="Option">Yes</item> </result> <result> <item VAL="Name">Elvis Kans</item> <group> <header VAL="Total">5</header> <item VAL="Item_1">3</item> <item VAL="Item_2">2</item> </group> <item VAL="Estimate">10</item> <item VAL="Option">Yes</item> </result> </output>
现有XSLT 1.0代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output indent="yes"/> <xsl:strip-space elements="*"/> <xsl:key name="paymentKey" match="payment" use="concat(Account/@Id, Account/@Token)"/> <xsl:template match="Data"> <output> <xsl:apply-templates select="@*|payment[generate-id()=generate-id(key('paymentKey', concat(Account/@Id, Account/@Token))[1])]"/> </output> </xsl:template> <xsl:template match="payment"> <xsl:variable name="curr-group" select="key('paymentKey', concat(Account/@Id, Account/@Token))"/> <result> <xsl:choose> <xsl:when test="count($curr-group)=1"> <!-- Different Groups --> <xsl:element name="item"> <xsl:attribute name="VAL">Name</xsl:attribute> <xsl:value-of select="@Name"/> </xsl:element> <group> <xsl:element name="header"> <xsl:attribute name="VAL">Total</xsl:attribute> <xsl:value-of select="Amount/@Paid"/> </xsl:element> <xsl:for-each select="Amount/Breakout/Misc"> <xsl:element name="item"> <xsl:attribute name="VAL">Item_<xsl:value-of select="position()"/> </xsl:attribute> <xsl:value-of select="@Amount"/> </xsl:element> </xsl:for-each> </group> <xsl:element name="item"> <xsl:attribute name="VAL">Estimate</xsl:attribute> <xsl:value-of select="Estimate/@Val"/> </xsl:element> <xsl:element name="item"> <xsl:attribute name="VAL">Option</xsl:attribute> <xsl:value-of select="Option/@Val"/> </xsl:element> </xsl:when> <xsl:otherwise> <!-- Same Groups --> <xsl:element name="item"> <xsl:attribute name="VAL">Name</xsl:attribute> <xsl:value-of select="@Name"/> </xsl:element> <group> <xsl:element name="header"> <xsl:attribute name="VAL">Total</xsl:attribute> <xsl:value-of select="Amount/@Paid"/> </xsl:element> <xsl:for-each select="Amount/Breakout/Misc"> <xsl:element name="item"> <xsl:attribute name="VAL">Item_<xsl:value-of select="position()"/> </xsl:attribute> <xsl:value-of select="@Amount"/> </xsl:element> </xsl:for-each> </group> <xsl:element name="item"> <xsl:attribute name="VAL">Estimate</xsl:attribute> <xsl:value-of select="Estimate/@Val"/> </xsl:element> <xsl:element name="item"> <xsl:attribute name="VAL">Option</xsl:attribute> <xsl:value-of select="Option/@Val"/> </xsl:element> </xsl:otherwise> </xsl:choose> </result> </xsl:template> </xsl:stylesheet>
修改方案
核心问题是当前代码只从当前匹配的payment元素(组内第一个元素)取值,未遍历整个组查找非空值。调整为从整个curr-group中提取有效数据,具体修改如下:
- 移除冗余的
<xsl:choose>分支:单元素组和多元素组的处理逻辑可合并,统一从整个组取值。 - 从组内提取有效
<Amount>数据:用$curr-group/Amount[@Paid][1]定位组内第一个带Paid属性的Amount元素。 - 提取非空
<Estimate>值:用$curr-group/Estimate[@Val][1]/@Val获取组内第一个带Val属性的Estimate值。 - 提取存在的
<Option>值:用$curr-group/Option[@Val][1]/@Val获取组内第一个存在的Option值。
修改后的完整XSLT代码:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output indent="yes"/> <xsl:strip-space elements="*"/> <xsl:key name="paymentKey" match="payment" use="concat(Account/@Id, Account/@Token)"/> <xsl:template match="Data"> <output> <xsl:apply-templates select="payment[generate-id()=generate-id(key('paymentKey', concat(Account/@Id, Account/@Token))[1])]"/> </output> </xsl:template> <xsl:template match="payment"> <xsl:variable name="curr-group" select="key('paymentKey', concat(Account/@Id, Account/@Token))"/> <!-- 获取组内第一个有Paid属性的Amount元素 --> <xsl:variable name="valid-amount" select="$curr-group/Amount[@Paid][1]"/> <result> <item VAL="Name"> <xsl:value-of select="@Name"/> </item> <group> <header VAL="Total"> <xsl:value-of select="$valid-amount/@Paid"/> </header> <xsl:for-each select="$valid-amount/Breakout/Misc"> <item VAL="Item_{position()}"> <xsl:value-of select="@Amount"/> </item> </xsl:for-each> </group> <item VAL="Estimate"> <xsl:value-of select="$curr-group/Estimate[@Val][1]/@Val"/> </item> <item VAL="Option"> <xsl:value-of select="$curr-group/Option[@Val][1]/@Val"/> </item> </result> </xsl:template> </xsl:stylesheet>
修改说明
valid-amount变量:精准定位组内有效Amount元素,确保获取到支付金额和拆分明细。- 提取
Estimate和Option时,通过$curr-group/元素[@属性][1]的方式,从整个组中匹配第一个有效元素,避免仅从组内第一个payment取值。 - 移除
<xsl:choose>分支,简化代码结构,统一处理逻辑。
内容的提问来源于stack exchange,提问作者Somebody
相关产品推荐
相关产品推荐

