Search code examples
xsltxslt-1.0ibm-datapower

Need to select only the Unique value from xsl


Input XML with Multiple same key value:

<ns2:enumCollection>
    <ns3:item>
        <ns3:key>000</ns3:key>
        <ns3:value>GRS Tracker00CA1</ns3:value>
    </ns3:item>
    <ns3:item>
        <ns3:key>000</ns3:key>
        <ns3:value>GRS Tracker00CA1</ns3:value>
    </ns3:item>
    <ns3:item>
        <ns3:key>001</ns3:key>
        <ns3:value>GRS Tracker00CA2</ns3:value>
    </ns3:item>                             
</ns2:enumCollection>

Expected Output Result

<ns2:enumCollection>
    <ns3:item>
        <ns3:key>000</ns3:key>
        <ns3:value>GRS Tracker00CA1</ns3:value>
    </ns3:item>
    <ns3:item>
        <ns3:key>001</ns3:key>
        <ns3:value>GRS Tracker00CA2</ns3:value>
    </ns3:item>                             
</ns2:enumCollection>

Need Help on this Transformation. I am trying to use xsl:Key function but not able to transform successfully.


Solution

  • The first thing to note is that your input XML has namespace prefixes, but there are no declarations for them, which is not allowed. I am assuming your actual XML does have them though! For the purposes of this answer though, I am going to make up some namespaces

    <ns2:enumCollection xmlns:ns2="ns2" xmlns:ns3="ns3">
        <ns3:item>
            <ns3:key>000</ns3:key>
            <ns3:value>GRS Tracker00CA1</ns3:value>
        </ns3:item>
        <ns3:item>
            <ns3:key>000</ns3:key>
            <ns3:value>GRS Tracker00CA1</ns3:value>
        </ns3:item>
        <ns3:item>
            <ns3:key>001</ns3:key>
            <ns3:value>GRS Tracker00CA2</ns3:value>
        </ns3:item>                             
    </ns2:enumCollection>
    

    To get unique values, you can make use of a technique called Muenchian grouping as that involves getting the unique values initially, but in your case you are discarding the rest of the 'group'.

    If you are grouping items by key, then your XML should actually look like this

    <xsl:key name="element-key" match="ns3:item" use="ns3:key" /> 
    

    Then, to get your "distinct" values, you would do this to get the item for the first occurrence of each key

    Try this XSLT

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="ns2" xmlns:ns3="ns3">
        <xsl:output method="xml" indent="yes"/>
        <xsl:key name="element-key" match="ns3:item" use="ns3:key" /> 
    
        <xsl:template match="ns2:enumCollection">
           <xsl:copy>
              <xsl:apply-templates select="ns3:item[generate-id() = generate-id(key('element-key', ns3:key)[1])]"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="@*|node()">
           <xsl:copy>
               <xsl:apply-templates select="@*|node()"/>
           </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    Alternatively, you could have a template to 'discard' the elements that aren't first in each group

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="ns2" xmlns:ns3="ns3">
        <xsl:output method="xml" indent="yes"/>
        <xsl:key name="element-key" match="ns3:item" use="ns3:key" /> 
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="ns3:item[generate-id() != generate-id(key('element-key', ns3:key)[1])]" />
    </xsl:stylesheet>
    

    Both of these output the following

    <ns2:enumCollection xmlns:ns2="ns2" xmlns:ns3="ns3">
        <ns3:item>
            <ns3:key>000</ns3:key>
            <ns3:value>GRS Tracker00CA1</ns3:value>
        </ns3:item>
        <ns3:item>
            <ns3:key>001</ns3:key>
            <ns3:value>GRS Tracker00CA2</ns3:value>
        </ns3:item>
    </ns2:enumCollection>