Search code examples
xsltxslt-1.0xslt-2.0xslt-grouping

Separate duplicates from unique


I tried with many XSLT methods like Key, for-each-group and match method options, but it is not working for me.

Input XML:

<?xml version='1.0' encoding='UTF-8'?>
<CustomerRecord>
    <Customer>
        <chargeto>ABC</chargeto>
        <chargename>GARY</chargename>
    </Customer>
    <Customer>
        <chargeto>XYZ</chargeto>
        <chargename>DAVIS</chargename>
    </Customer>
    <Customer>
        <chargeto>CDE</chargeto>
        <chargename>GARY DAVIS</chargename>
    </Customer>
    <Customer>
        <chargeto>ABC</chargeto>
        <chargename>DAV</chargename>
    </Customer>
</CustomerRecord>

Expected output XML:

<?xml version='1.0' encoding='UTF-8'?>
<Root>
<Customer_PO>
    <Customer>
        <chargeto>ABC</chargeto>
        <chargename>GARY</chargename>
    </Customer>
    <Customer>
        <chargeto>ABC</chargeto>
        <chargename>DAV</chargename>
    </Customer>
</Customer_PO>
<Customer_Falty>
    <Customer>
        <chargeto>XYZ</chargeto>
        <chargename>DAVIS</chargename>
    </Customer>
    <Customer>
        <chargeto>CDE</chargeto>
        <chargename>GARY DAVIS</chargename>
    </Customer>
</Customer_Falty>
</Root>

Below is the XSLT which I wrote Initially to at least get the some details in the Output, but data is getting missed when the target is generated:


    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var" exclude-result-prefixes="msxsl var" version="1.0" xmlns:ns0="http://Namespace">
      <xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
      <xsl:key use="chargeto" match="/CustomerRecord/Customer" name ="groups"/>
      
      <xsl:template match="/">
        <xsl:apply-templates select="/CustomerRecord" />
      </xsl:template>
      <xsl:template match="/CustomerRecord">
        <Root>
         <xsl:for-each select="/CustomerRecord/Customer[generate-id(.)=generate-id(key('groups',chargeto))]">      
            <Customer_PO>
              <xsl:for-each select="key('groups',chargeto)">
              <Customer>
                <chargeto>
                  <xsl:value-of select="CustomerRecord/chargeto/text()" />
                </chargeto>
                <chargename>
                  <xsl:value-of select="CustomerRecord/chargename/text()" />
                </chargename>
              </Customer>
            </xsl:for-each>  
            </Customer_PO>
          </xsl:for-each>
        </Root>
      </xsl:template>
    </xsl:stylesheet>

Please find the explanation about the Expected Output

<Customer_PO>. Under this tag we will have all the <Customer> segments where all the <chargeto> fields are having duplicate value

<Customer_Falty> Under this tag we will have all the <Customer> segment where all the <chargeto> field values are unique


Solution

  • If I understand correctly your explanation, you want to do something like:

    XSL 1.0

    <xsl:stylesheet version="1.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="cust-by-charge" match="Customer" use="chargeto" />
    
    <xsl:template match="/CustomerRecord">
        <Root>
            <Customer_PO>
                <xsl:copy-of select="Customer[count(key('cust-by-charge', chargeto)) > 1]"/>
            </Customer_PO>
            <Customer_Falty>
                <xsl:copy-of select="Customer[count(key('cust-by-charge', chargeto)) = 1]"/>
            </Customer_Falty>
        </Root>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Possibly there's a more elegant approach that would count the size of each group only once.