Create an XSLT Map to Read Multiple Correlated Payloads

You can create XSLT maps to loop thought different sources (input payloads) with instances that are correlated by key fields.

Example for 1:0..n and 1:1 Relationships Between Sources

The following business units and employees example is provided:
  • Each business unit can have 0..n employees (1:0..n relationship).
  • The G/L accounts source with a 1:1 correlation with business units.

You can create an XSLT map that combines them.

The sources (input payloads) for this example are as follows:

  • $BusinessUnits
    <company>
      <bu> 
        <id>SD</id> <name>Software Development</name> 
        <accounbtid>i9</accountid> 
      </bu> 
      <bu> 
        <id>BS</id> <name>Sales</name> 
        <accounbtid>i1</accountid>                
      </bu>
      <bu> 
        <id>MD</id> <name>Marketing</name>  
        <accounbtid>i2</accountid>          
      </bu>
     </company>
  • $Employees
    <people>
      <emp> <buid>SD</buid> <name>Joe Smith</name> </emp> 
      <emp> <buid>SD</buid> <name>Mike Jones</name> </emp>
      <emp> <buid>BS</buid> <name>Dave Johnson</name> </emp> 
     </people>
  • $GLAccounts
    <gl>     
       <account> <id>i1</id> <number>001.345</number> </account> 
       <account> <id>i2</id> <number>001.477</number> </account>
       <account> <id>i9</id> <number>001.223</number> </account>
    </gl>

The link between $BusinessUnits and $Employees is the business unit ID. The header is $BusinessUnit and the detail is $Employees. The link for the GL accounts and business units is the account ID.

The following output is needed:
<xxx>
  <yyy> 
    <BU id='SD'>Software Development</BU> 
    <empName>Joe Smith</empName> 
    <accNumber>001.223</accNumber>
  </yyy> 
  <yyy> 
    <BU id='SD'>Software Development</BU> 
    <empName>Mike Jones</empName>    
    <accNumber>001.223</accNumber>
  </yyy>
  <yyy> 
    <BU id='BS'>Sales</BU>         
    <empName>Dave Johnson</empName> 
    <accNumber>001.345</accNumber>
  </yyy> 
</xxx>

Solution

When the instances (records) of the sources have a 1:1 correlation, you can use a predicate.

When the instances have 1:0..n correlation, using an xsl:for-each-group performs better than using predicates because it avoids overparsing the source.

The XSLT content is as follows:

<?xml version = '1.0' encoding = 'UTF-8'?>
<xsl:stylesheet version="2.0"  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:fn="http://www.w3.org/2005/xpath-functions"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:param name="BusinessUnits" />
  <xsl:param name="Employees" />
  <xsl:param name="GLAccounts"/>
  <xsl:template match="/" >
     <xxx>
        <xsl:for-each-group select="$Employees/people/employee" group-by="buid">
           <!-- this section will be executed only once per 'buid' -->
           <!-- Store the Business Unit Record in a variable -->
           <xsl:variable name="BURecord">
               <xsl:copy-of select="$BusinessUnits/company/bu[id = fn:current-grouping-key()]"/>
           </xsl:variable>
           <!-- Store the GL Account Record in a variable -->
           <xsl:variable name="GLAccountRecord">
               <xsl:copy-of select="$GLAccounts/gl/account[id = $BURecord/bu/accountid]" />
           </xsl:variable>
           <!-- end: executed only once per 'buid' -->
           <xsl:for-each select="current-group()">
               <!-- iterates the employees within the current 'buid' -->
               <yyy>
                   <BU id="{./buid}">
                       <xsl:value-of select="$BURecord/bu/name" />
                   </BU>
                   <empName>
                       <xsl:value-of select="./name" />
                   </empName>
                   <accNumber>
                        <xsl:value-of select="$GLAccountRecord/account/number"/>
                   </accNumber>
               </yyy>
            </xsl:for-each>
        </xsl:for-each-group>
     </xxx>
  </xsl:template>
</xsl:stylesheet>

Summary

  • When there is a 1:1 relationship, using predicates instead of <xsl:for-each-group> is faster because XSLT does not need to sort the data to create the group.
  • When there is a 1:0..n relationship, using <xsl:for-each-group> performs faster than using predicates. This is because predicates, in the above example, parse the entire business unit source and GL account source per every employee.