Question

I have an XML of that kind:

<?xml version="1.0" encoding="UTF-8"?>
<Returns>
 <Row>
  <Application>
   app1
  </Application>
  <Component>
   comp1
  </Component>
  <Version>
   1
  </Version>
 </Row>
 <Row>
  <Application>
   app1
  </Application>
  <Component>
   comp2
  </Component>
  <Version>
   1
  </Version>
 </Row>
 <Row>
  <Application>
   app1
  </Application>
  <Component>
   comp1
  </Component>
  <Version>
   2
  </Version>
 </Row>
 <Row>
  <Application>
   app1
  </Application>
  <Component>
   comp2
  </Component>
  <Version>
   2
  </Version>
 </Row>
 <Row>
  <Application>
   app2
  </Application>
  <Component>
   comp1
  </Component>
  <Version>
   1
  </Version>
 </Row>
  <Row>
  <Application>
   app2
  </Application>
  <Component>
   comp2
  </Component>
  <Version>
   1
  </Version>
 </Row>
  <Row>
  <Application>
   app2
  </Application>
  <Component>
   comp1
  </Component>
  <Version>
   2
  </Version>
 </Row>
  <Row>
  <Application>
   app2
  </Application>
  <Component>
   comp2
  </Component>
  <Version>
   2
  </Version>
 </Row>
</Returns>

I need to transform it to something like this:

    <?xml version="1.0" encoding="UTF-8"?>
<Components>
   <Component application="app1" version="2" name="comp1"/>
   <Component application="app1" version="2" name="comp2"/>
   <Component application="app2" version="2" name="comp1"/>
   <Component application="app2" version="2" name="comp2"/>
</Components>

The goal is to show only the components that belong to the latest version of each application.

I wrote this XSL template that uses muenchian grouping:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:key name="application" match="Row" use="Application" />
<xsl:template match="Returns">
    <Components>
        <xsl:apply-templates select="Row[generate-id(.)=generate-id(key('application',Application)[1])]">
        </xsl:apply-templates>
    </Components>
</xsl:template>
 <xsl:template match="Row">
        <xsl:for-each select="key('application', Application)">
                    <xsl:sort select="Version" order="descending"/>
                  <xsl:for-each select="Version">
                    <Component application="{normalize-space(../Application)}" version="{normalize-space(current())}" name="{normalize-space(../Component)}"></Component>                      
                 </xsl:for-each>
        </xsl:for-each>
</xsl:template>

And now I get this output:

<?xml version="1.0" encoding="UTF-8"?>
<Components>
   <Component application="app1" version="2" name="comp1"/>
   <Component application="app1" version="2" name="comp2"/>
   <Component application="app1" version="1" name="comp1"/>
   <Component application="app1" version="1" name="comp2"/>
   <Component application="app2" version="2" name="comp1"/>
   <Component application="app2" version="2" name="comp2"/>
   <Component application="app2" version="1" name="comp1"/>
   <Component application="app2" version="1" name="comp2"/>
</Components>

How can I filter the components to get only the ones that belong to the latest version of the same application?

Was it helpful?

Solution

Within your template that matches Row (for each distinct "application"), you will probably need to add a variable, which contains the value of the latest version for the current group. There is no "max" function available in XSLT 1.0, so you will have to do it like this

    <xsl:variable name="Version">
       <xsl:for-each select="key('application', Application)">
           <xsl:sort select="Version" order="descending"/>
           <xsl:if test="position() = 1">
               <xsl:value-of select="Version" />
           </xsl:if>
        </xsl:for-each>
    </xsl:variable>

Then you can get the elements in the current group with the latest version like so

<xsl:for-each select="key('application', Application)[Version=$Version]">

Try this XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:key name="application" match="Row" use="Application" />
<xsl:template match="Returns">
    <Components>
        <xsl:apply-templates select="Row[generate-id(.)=generate-id(key('application',Application)[1])]">
        </xsl:apply-templates>
    </Components>
</xsl:template>
 <xsl:template match="Row">
        <xsl:variable name="Version">
           <xsl:for-each select="key('application', Application)">
               <xsl:sort select="Version" order="descending"/>
               <xsl:if test="position() = 1">
                   <xsl:value-of select="Version" />
               </xsl:if>
            </xsl:for-each>
        </xsl:variable>
        <xsl:for-each select="key('application', Application)[Version=$Version]">
            <Component application="{normalize-space(Application)}" version="{normalize-space(Version)}" name="{normalize-space(Component)}"></Component>                      
        </xsl:for-each>
</xsl:template>
</xsl:stylesheet>

This produces the following output

<Components>
  <Component application="app1" version="2" name="comp1"/>
  <Component application="app1" version="2" name="comp2"/>
  <Component application="app2" version="2" name="comp1"/>
  <Component application="app2" version="2" name="comp2"/>
</Components>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top