How to group and sum values in XSLT

匿名 (未验证) 提交于 2019-12-03 08:35:02

问题:

For each "agency" node I need to find the "stmt" elements that have the same key1, key2, key3 values and output just one "stmt" node with the "comm" and "prem" values summed together. For any "stmt" elements within that "agency" that don't match any other "stmt" elements based on key1, key2 and key3 I need to output them as is. So after transformation the first "agency" node would only have two "stmt" nodes (one summed) and the second "agency" node would be passed as is because the keys don't match. XSLT 1.0 or 2.0 solutions are ok...though my stylesheet is currently 1.0. Note that the agency nodes could have any number of "stmt" elements that have matching keys which need to be grouped and summed and any number that don't.

<statement> <agency>     <stmt>         <key1>1234</key1>         <key2>ABC</key2>         <key3>15.000</key3>         <comm>75.00</comm>         <prem>100.00</prem>     </stmt>     <stmt>         <key1>1234</key1>         <key2>ABC</key2>         <key3>15.000</key3>         <comm>25.00</comm>         <prem>200.00</prem>     </stmt>     <stmt>         <key1>1234</key1>         <key2>ABC</key2>         <key3>17.50</key3>         <comm>25.00</comm>         <prem>100.00</prem>     </stmt> </agency> <agency>     <stmt>         <key1>5678</key1>         <key2>DEF</key2>         <key3>15.000</key3>         <comm>10.00</comm>         <prem>20.00</prem>     </stmt>     <stmt>         <key1>5678</key1>         <key2>DEF</key2>         <key3>17.000</key3>         <comm>15.00</comm>         <prem>12.00</prem>     </stmt> </agency> 

回答1:

And an XSLT 2.0 solution:

<xsl:stylesheet version="2.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:xs="http://www.w3.org/2001/XMLSchema"  exclude-result-prefixes="xs"  >  <xsl:output omit-xml-declaration="yes" indent="yes"/>   <xsl:template match="node()|@*">    <xsl:copy>     <xsl:apply-templates select="node()|@*"/>    </xsl:copy>  </xsl:template>   <xsl:template match="agency">   <agency>    <xsl:for-each-group select="stmt" group-by=     "concat(key1, '+', key2, '+', key3)">      <stmt>       <xsl:copy-of select=        "current-group()[1]/*[starts-with(name(),'key')]"/>         <comm>          <xsl:value-of select="sum(current-group()/comm)"/>        </comm>        <prem>          <xsl:value-of select="sum(current-group()/prem)"/>        </prem>     </stmt>    </xsl:for-each-group>   </agency>  </xsl:template> </xsl:stylesheet> 


回答2:

In XSLT 1.0 use the Muenchian method for grouping (with compound key).

This transformation:

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">  <xsl:output omit-xml-declaration="yes" indent="yes"/>   <xsl:key name="kStmtByKeys" match="stmt"       use="concat(generate-id(..), key1, '+', key2, '+', key3)"/>   <xsl:template match="node()|@*">    <xsl:copy>     <xsl:apply-templates select="node()|@*"/>    </xsl:copy>  </xsl:template>   <xsl:template match="agency">    <agency>     <xsl:for-each select=      "stmt[generate-id()           =            generate-id(key('kStmtByKeys',                            concat(generate-id(..), key1, '+', key2, '+', key3)                            )[1]                        )            ]      ">       <xsl:variable name="vkeyGroup" select=        "key('kStmtByKeys', concat(generate-id(..), key1, '+', key2, '+', key3))"/>       <stmt>       <xsl:copy-of select="*[starts-with(name(), 'key')]"/>       <comm>        <xsl:value-of select="sum($vkeyGroup/comm)"/>       </comm>       <prem>        <xsl:value-of select="sum($vkeyGroup/prem)"/>       </prem>      </stmt>     </xsl:for-each>    </agency>  </xsl:template> </xsl:stylesheet> 

when applied on the provided XML document, produces the wanted result:

<statement>     <agency>         <stmt>             <key1>1234</key1>             <key2>ABC</key2>             <key3>15.000</key3>             <comm>100</comm>             <prem>300</prem>         </stmt>         <stmt>             <key1>1234</key1>             <key2>ABC</key2>             <key3>17.50</key3>             <comm>25</comm>             <prem>100</prem>         </stmt>     </agency>     <agency>         <stmt>             <key1>5678</key1>             <key2>DEF</key2>             <key3>15.000</key3>             <comm>10</comm>             <prem>20</prem>         </stmt>         <stmt>             <key1>5678</key1>             <key2>DEF</key2>             <key3>17.000</key3>             <comm>15</comm>             <prem>12</prem>         </stmt>     </agency> </statement> 


回答3:

<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">     <xsl:output method="xml" indent="yes"/>      <xsl:template match="/|*">         <xsl:copy>             <xsl:apply-templates select="*" />         </xsl:copy>     </xsl:template>      <xsl:template match="stmt">         <xsl:variable name="stmtGroup" select="../stmt[(key1=current()/key1) and (key2=current()/key2) and (key3=current()/key3)]" />         <xsl:if test="generate-id()=generate-id($stmtGroup[1])">             <xsl:copy>                 <key1>                     <xsl:value-of select="key1"/>                 </key1>                 <key2>                     <xsl:value-of select="key2"/>                 </key2>                 <key3>                     <xsl:value-of select="key3"/>                 </key3>                 <comm>                     <xsl:value-of select="format-number(sum($stmtGroup/comm), '#.00')"/>                 </comm>                 <prem>                     <xsl:value-of select="format-number(sum($stmtGroup/prem), '#.00')"/>                 </prem>             </xsl:copy>         </xsl:if>     </xsl:template> </xsl:stylesheet> 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!