Search code examples
xsltxslt-1.0xslt-grouping

for-each and sort and group the xml using xslt


I have an XML as below. For each test I need to sort cat and title and group . For the cat value abc i need to get title and set values. Any set values matches the need to add the title value inside the set node in ascending order. here i did hard coded the cat value.But in my scenario the cat value is getting from external source.

<?xml version="1.0" encoding="utf-8" ?>
<compound>
  <test>
    <title>k</title>
    <cetval>
      <cat>abc</cat>
      <cat>fcg</cat>
    </cetval>
    <value>1</value>
    <set>
      <color>blue</color>
    </set>
  </test>
  <test>
    <title>p</title>
    <cetval>
      <cat>fcg</cat>
      <cat>klm</cat>
    </cetval>

    <value>10</value>
    <set>
      <color>pink</color>
    </set>
  </test>
  <test>
    <title>j</title>
    <cetval>
      <cat>fcg</cat>
      <cat>klm</cat>
      <cat>abc</cat>
    </cetval>
    <value>484</value>
    <set>
      <color>yellow</color>
    </set>
  </test>
  <test>
    <title>v</title>
    <cetval>
      <cat>dji</cat>
      <cat>kfjsdlk</cat>
    </cetval>
    <value>93</value>

  </test>
  <test>
    <title>r</title>
    <cetval>
      <cat>deiu</cat>
      <cat>kfjdf</cat>
      <cat>abc</cat>
    </cetval>
    <value>10</value>
    <set>
      <color>blue</color>
    </set>
    <test>
      <title>i</title>
      <cetval>
        <cat>fcg</cat>
        <cat>klm</cat>
        <cat>abc</cat>
      </cetval>

      <value>10</value>

    </test>
    <test>
      <title>w</title>
      <cetval>
        <cat>diweif</cat>
        <cat>fjf</cat>
        <cat>abc</cat>
      </cetval>
      <value>10</value>
      <set>
        <color>yellow</color>
      </set>
    </test>
  </test>

</compound>    

I need the output as below:

<?xml version="1.0" encoding="utf-8" ?>
<compound>
  <cat>abc</cat>
  <set>
    <color>blue</color>
    <title>k</title>
    <title>r</title>
  </set>
  <set>
    <color>yellow</color>
    <title>j</title>
    <title>w</title>
  </set>
  <title>i</title>


</compound>

My transform looks like below:

<?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:for-each select="compound/test">
        <xsl:sort select="./cetval/cat" order="ascending"/>
        <xsl:sort select="./title" order="ascending"/>
        <xsl:for-each select="./cetval/cat">
          <xsl:choose>
            <xsl:when test="./cat = 'abc' > 0">

              <xsl:value-of select="ancestor::test/title"/>

            </xsl:when>
          </xsl:choose>
        </xsl:for-each>

        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Can any one suggest me how to generate the required output?


Solution

  • Ok, I think I've made sense of your requirements. How's this:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
      <xsl:key name="kColor" match="test" use="set/color" />
      <xsl:param name="catValue" select="'abc'" />
    
      <xsl:template match="/*">
        <xsl:copy>
          <cat>
            <xsl:value-of select ="$catValue"/>
          </cat>
          <xsl:apply-templates 
            select="//test[generate-id() = 
                           generate-id(
                                key('kColor', set/color)
                                      [cetval/cat = $catValue][1]
                                      )
                          ]" />
          <xsl:apply-templates 
            select="//test[cetval/cat = $catValue and not(set/color)]
                          /title">
            <xsl:sort select="."/>
          </xsl:apply-templates>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="test">
        <set>
          <xsl:apply-templates select="set/color" />
          <xsl:apply-templates
            select="key('kColor', set/color)[cetval/cat = $catValue]/title">
            <xsl:sort select="." />
          </xsl:apply-templates>
        </set>
      </xsl:template>
    
      <xsl:template match="@* | node()">
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:template>
    </xsl:stylesheet>
    

    Here $catValue is specified as a parameter so you can pass a different value in from externally. When this is run on your sample XML, the result is:

    <compound>
      <cat>abc</cat>
      <set>
        <color>blue</color>
        <title>k</title>
        <title>r</title>
      </set>
      <set>
        <color>yellow</color>
        <title>j</title>
        <title>w</title>
      </set>
      <title>i</title>
    </compound>