SP de Wit
SP de Wit

Reputation: 215

How to join two node sets based on unique element?

I have one big xml file with different node sets that I need to combine using xsl based on a unique element value that both nodes contain.

Here's a sample of the xml file that needs to be transformed:

<root>
  <node1>
    <funds>
      <fund>
        <FundId>a</FundId>
        <FundName>fund a</FundName>
        <SomeInfo>some info</SomeInfo>
      </fund>
      <fund>
        <FundId>b</FundId>
        <FundName>fund b</FundName>
        <SomeInfo>some info</SomeInfo>
      </fund>
      <fund>
        <FundId>c</FundId>
        <FundName>fund c</FundName>
        <SomeInfo>some info</SomeInfo>
      </fund>
    </funds>
  </node1>
  <node2>
    <funds>
      <fund>
        <FundId>a</FundId>
        <MaxInvestmentAmount>200</MaxInvestmentAmount>
        <MinInvestmentAmount>1</MinInvestmentAmount>
      </fund>
      <fund>
        <FundId>b</FundId>
        <MaxInvestmentAmount>100</MaxInvestmentAmount>
        <MinInvestmentAmount>5</MinInvestmentAmount>
      </fund>
      <fund>
        <FundId>c</FundId>
        <MaxInvestmentAmount>50</MaxInvestmentAmount>
        <MinInvestmentAmount>20</MinInvestmentAmount>
      </fund>
    </funds>
  </node2>
</root>

And here's the desired output:

<node>
    <funds>
        <fund>
            <FundId>a<FundId/>
            <FundName>fund a</FundName>
            <SomeInfo>some info</SomeInfo>
            <MaxInvestmentAmount>200</MaxInvestmentAmount>
            <MinInvestmentAmount>1</MinInvestmentAmount>
        </fund>
        <fund>
            <FundId>b<FundId/>
            <FundName>fund b</FundName>
            <SomeInfo>some info</SomeInfo>
            <MaxInvestmentAmount>100</MaxInvestmentAmount>
            <MinInvestmentAmount>5</MinInvestmentAmount>
        </fund>
        <fund>
            <FundId>c<FundId/>
            <FundName>fund c</FundName>
            <SomeInfo>some info</SomeInfo>
            <MaxInvestmentAmount>50</MaxInvestmentAmount>
            <MinInvestmentAmount>20</MinInvestmentAmount>
        </fund>
    </funds>
</node>

I've tried template matching but this doesn't seem to work the way I have tried it since both nodes have the same inner node names so they keep overriding each other.

Upvotes: 0

Views: 196

Answers (1)

zx485
zx485

Reputation: 29022

A solid method to achieve this is by using an xsl:key over the <fund> elements with the FundId as key. One relevant restriction of this approach is that only the FundId keys of the first child element - here <node1> - are merged. If other child elements contain more values, this method will not work as expected.

Here is the XSLT-1.0 stylesheet:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" />
  <xsl:key name="keyFund" match="fund" use="FundId" />   

  <xsl:template match="/root">
    <node>
        <funds>
            <xsl:for-each select="*[1]/funds/fund">
                <fund>
                    <xsl:copy-of select="FundId" />
                    <xsl:for-each select="key('keyFund',FundId)">
                        <xsl:copy-of select="current()/*[not(self::FundId)]" />
                    </xsl:for-each>
                </fund>
            </xsl:for-each>
        </funds>
    </node>
  </xsl:template>

</xsl:stylesheet>

Its output is:

<?xml version="1.0"?>
<node>
    <funds>
        <fund>
            <FundId>a</FundId>
            <FundName>fund a</FundName>
            <SomeInfo>some info</SomeInfo>
            <MaxInvestmentAmount>200</MaxInvestmentAmount>
            <MinInvestmentAmount>1</MinInvestmentAmount>
        </fund>
        <fund>
            <FundId>b</FundId>
            <FundName>fund b</FundName>
            <SomeInfo>some info</SomeInfo>
            <MaxInvestmentAmount>100</MaxInvestmentAmount>
            <MinInvestmentAmount>5</MinInvestmentAmount>
        </fund>
        <fund>
            <FundId>c</FundId>
            <FundName>fund c</FundName>
            <SomeInfo>some info</SomeInfo>
            <MaxInvestmentAmount>50</MaxInvestmentAmount>
            <MinInvestmentAmount>20</MinInvestmentAmount>
        </fund>
    </funds>
</node>

Upvotes: 1

Related Questions