Ravi Gupta
Ravi Gupta

Reputation: 67

XSLT grouping / merging - two different nodes into one

Struggling with an xslt transformation. Basically merging two different nodes based on a key (country code) so input xml has two parts.

If from part 1, country is found in part 2,
result = found

if from part 1, country is not found in part 2,
 result = new 

if from part 2, country is not found in part1,
 result = not found

Input XML -

<Root>
    <SomeData/>
    <COUNTRIES>
        <VALID_COUNTRY>
            <COUNTRY>DK</COUNTRY>
            <LANGUAGE>DANISH</LANGUAGE>
        </VALID_COUNTRY>
        <VALID_COUNTRY>
            <COUNTRY>SE</COUNTRY>
            <LANGUAGE>SWEDISH</LANGUAGE>
        </VALID_COUNTRY>
        <VALID_COUNTRY>
            <COUNTRY>CA</COUNTRY>
            <LANGUAGE>ENGLISH</LANGUAGE>
        </VALID_COUNTRY>
        <VALID_COUNTRY>
            <COUNTRY>US</COUNTRY>
            <LANGUAGE>ENGLISH</LANGUAGE>
        </VALID_COUNTRY>
    </COUNTRIES>
    <SomeOtherData/>
    <DATA>
        <FURTHER>
            <CountryInfo>
                <Country>DK</Country>
            </CountryInfo>
            <CountryInfo>
                <Country>US</Country>
            </CountryInfo>
            <CountryInfo>
                <Country>UK</Country>
            </CountryInfo>          
            <CountryInfo>
                <Country>AU</Country>
            </CountryInfo>  
        </FURTHER>
    </DATA>
</Root>

Output XML

<Root>
    <SomeData/>
    <COUNTRIES>
        <VALID_COUNTRY>
            <COUNTRY>DK</COUNTRY>
            <LANGUAGE>DANISH</LANGUAGE>
            <FOUND>YES</FOUND>
        </VALID_COUNTRY>
        <VALID_COUNTRY>
            <COUNTRY>SE</COUNTRY>
            <LANGUAGE>SWEDISH</LANGUAGE>
            <FOUND>NEW COUNTRY</FOUND>
        </VALID_COUNTRY>
        <VALID_COUNTRY>
            <COUNTRY>CA</COUNTRY>
            <LANGUAGE>ENGLISH</LANGUAGE>
            <FOUND>NEW COUNTRY</FOUND>
        </VALID_COUNTRY>
        <VALID_COUNTRY>
            <COUNTRY>US</COUNTRY>
            <LANGUAGE>ENGLISH</LANGUAGE>
            <FOUND>YES</FOUND>
        </VALID_COUNTRY>

        <VALID_COUNTRY>
            <COUNTRY>UK</COUNTRY>

            <FOUND>NOT FOUND</FOUND>
        </VALID_COUNTRY>

        <VALID_COUNTRY>
            <COUNTRY>AU</COUNTRY>

            <FOUND>NOT FOUND</FOUND>
        </VALID_COUNTRY>        

    </COUNTRIES>
    <SomeOtherData/>
    <DATA>
        <FURTHER>
            <CountryInfo>
                <Country>DK</Country>
            </CountryInfo>
            <CountryInfo>
                <Country>US</Country>
            </CountryInfo>
            <CountryInfo>
                <Country>UK</Country>
            </CountryInfo>
            <CountryInfo>
                <Country>AU</Country>
            </CountryInfo>
        </FURTHER>
    </DATA>
</Root>

have tried using xslt group-by (for 2.0) and key function along with generate id, but not getting the right output. I can use xslt 3.0 for this one.

Here is the code, I have tried,

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:hci="http://sap.com/it/" exclude-result-prefixes="hci">
    <xsl:output method="xml" indent="yes"/>
    <!-- Identity template : copy all text nodes, elements and attributes -->
    <xsl:key name="CountryKey" match="//FURTHER/CountryInfo" use="Country"/>
    <xsl:variable name="tempCountry" select="./COUNTRY"/>
    <xsl:template match="/*">
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="VALID_COUNTRY">
        <xsl:for-each select="//VALID_COUNTRY[generate-id()=generate-id(key('CountryKey', ./COUNTRY)[1])]">
            <VALID_COUNTRY>     <xsl:value-of select="./COUNTRY"></xsl:value-of>        </VALID_COUNTRY>
            <LANGUAGE><xsl:value-of select="./LANGUAGE"></xsl:value-of></LANGUAGE>

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

Upvotes: 0

Views: 361

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167716

As you say you can use XSLT 2 or 3 you could use for-each-group in any case or xsl:merge with XSLT 3; grouping would be

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:mode name="add" on-no-match="shallow-copy"/>

  <xsl:template match="COUNTRIES">
      <xsl:copy>
          <xsl:for-each-group select="VALID_COUNTRY, /Root/DATA/FURTHER/CountryInfo" group-by="COUNTRY, Country">
              <xsl:apply-templates select="." mode="add"/>
          </xsl:for-each-group>
      </xsl:copy>
  </xsl:template>

  <xsl:template match="VALID_COUNTRY/LANGUAGE" mode="add">
      <xsl:copy-of select="."/>
      <FOUND>
          <xsl:value-of select="if (current-group()[2]) then 'YES' else 'NEW COUNTRY'"/>
      </FOUND>
  </xsl:template>

  <xsl:template match="CountryInfo" mode="add">
      <VALID_COUNTRY>
          <COUNTRY>
              <xsl:value-of select="Country"/>
          </COUNTRY>
          <FOUND>NOT FOUND</FOUND>
      </VALID_COUNTRY>
  </xsl:template>

https://xsltfiddle.liberty-development.net/ejivJse

Upvotes: 2

Related Questions