Search code examples
xmlxsltxslt-groupingxslkey

XSLT Key Grouping by considering multiple nodes based on a Condition


<root>
   <Entry>
      <ID>1</ID>
      <Details>
         <Code>A1</Code>
         <Value>1000</Value>
         <Indicator>1</Indicator>
      </Details>
   </Entry>

   <Entry>
      <ID>2</ID>
      <Details>
         <Code>A2</Code>
         <Value>2000</Value>
         <Indicator>2</Indicator>
      </Details>
   </Entry>

   <Entry>
      <ID>3</ID>
      <Details>
         <Code>A3</Code>
         <Value>2500</Value>
         <Indicator>3</Indicator>
      </Details>
   </Entry>

   <Entry>
      <ID>4</ID>
      <Details>
         <Code>B1</Code>
         <Value>3000</Value>
         <Indicator>0</Indicator>
      </Details>
   </Entry>

   <Entry>
      <ID>5</ID>
      <Details>
         <Code>B2</Code>
         <Value>4000</Value>
         <Indicator>5</Indicator>
      </Details>
   </Entry>

   <Entry>
      <ID>6</ID>
      <Details>
         <Code>B3</Code>
         <Value>4500</Value>
         <Indicator>7</Indicator>
      </Details>
   </Entry>
</root>

I have this input XML, which is a continuation to my earlier question. Basically I am looking to group the nodes based on the value of the <Code> node. The mapping is as follows:

  1. Codes 'A1', 'A2' and 'A3' needs to be grouped together (lets say Code-group 'A')
  2. Codes 'B1', 'B2' and 'B3' needs to be grouped together (lets say Code-group 'B')

[These codes are generic meant only as example and the actual codes are different and not so simple, please do not consider it literally]

I am eventually summing the values coming out of <Value> nodes in these groups but in addition to my previous question I am only summing if the <Indicator> node value is greater than or equal to 2

I currently use this XSLT (credits: michael.hor257k):

<xsl:stylesheet version="2.0" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="entry" match="Entry" use="Details/Code"/>

<xsl:template match="/root">
    <Output>
        <Code-group> A </Code-group>
        <Sum>
            <xsl:value-of select="sum(key('entry', ('A1', 'A2', 'A3'))/Details/Value)" />
        </Sum>
        <Code-group> B </Code-group>
        <Sum>
            <xsl:value-of select="sum(key('entry', ('B1', 'B2', 'B3'))/Details/Value)" />
        </Sum>
    </Output>
</xsl:template>

</xsl:stylesheet>

Which produces Output as follows:

<Output>
  <Code-group> A </Code-group>
  <Sum> 5500 </Sum>

  <Code-group> B </Code-group>
  <Sum> 11500 </Sum>
</Output>

But I need this Output for the scenario:

<Output>
  <Code-group> A </Code-group>
  <Sum> 4500 </Sum>

  <Code-group> B </Code-group>
  <Sum> 8500 </Sum>
</Output>

That is sum the <Value> node value in the respective Code-groups only when the <Indicator> is greater than or equal to 2

Is there a way to include this relational operator while defining the Key or should it be realised as 2 keys? Appreciate any inputs

Thanks in Advance!


Solution

  • It seems you only need a small modification:

    XSLT 2.0

    <xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
    <xsl:key name="entry" match="Details[Indicator >= 2]" use="Code"/>
    
    <xsl:template match="/root">
        <Output>
            <Code-group> A </Code-group>
            <Sum>
                <xsl:value-of select="sum(key('entry', ('A1', 'A2', 'A3'))/Value)" />
            </Sum>
            <Code-group> B </Code-group>
            <Sum>
                <xsl:value-of select="sum(key('entry', ('B1', 'B2', 'B3'))/Value)" />
            </Sum>
        </Output>
    </xsl:template>
    
    </xsl:stylesheet>