xmlxsltkeyapply-templates

XSLT match entry in another XML node and return a corresponding element


Disclaimer: I'm still learning XML / XSLT, so apologies if this is simple.

I'm trying to look up an element in node A, match it to a similar element in node B, and return an entry from node B to node A.

i.e. find the startDate under EmployeeTimeSheetEntry that matches EmployeeTimeValuationResult/bookingDate, and return the corresponding cust_Overtime_HR_Action element:

enter image description here

I'm trying to use a key to generate a lookup (based on the answer to this question: Print comma separated tokens when they have been looked up in another node), but I'm very unclear as to where to put the apply-templates statement to go and retrieve the node (and I'm very much confusing myself now, going round in circles).

Sample source XML:

<EmployeeTimeSheet>
<EmployeeTimeSheet>
  <employeeTimeSheetEntry>
    <EmployeeTimeSheetEntry>
      <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION>
      <startDate>2024-11-25T00:00:00.000</startDate>
    </EmployeeTimeSheetEntry>
    <EmployeeTimeSheetEntry>
      <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION>
      <startDate>2024-11-27T00:00:00.000</startDate>
    </EmployeeTimeSheetEntry>
    <EmployeeTimeSheetEntry>
      <cust_Overtime_HR_ACTION>Payout</cust_Overtime_HR_ACTION>
      <startDate>2024-11-30T00:00:00.000</startDate>
    </EmployeeTimeSheetEntry>
  </employeeTimeSheetEntry>
  <employeeTimeValuationResult>
    <EmployeeTimeValuationResult>
      <bookingDate>2024-11-25T00:00:00.000</bookingDate>
    </EmployeeTimeValuationResult>
  </employeeTimeValuationResult>
</EmployeeTimeSheet>

And this is what I'm trying (and failing) to do in XSLT:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- generate lookup table - must be top level, i.e. before template match -->
<xsl:key name="timeSheetEntry" match="EmployeeTimeSheet/EmployeeTimeSheet/employeeTimeSheetEntry/EmployeeTimeSheetEntry" use="startDate"/>

<xsl:template match="/">

<EmployeeTimeSheet>

<!-- retrieve cust_Overtime_HR_ACTION where the bookingDate matches the startDate in above lookup -->
<xsl:template match="/">
    <xsl:apply-templates select="key('timeSheetEntry', EmployeeTimeSheet/EmployeeTimeSheet/employeeTimeValuationResult/EmployeeTimeValuationResult/bookingDate)/cust_Overtime_HR_ACTION"/>
</xsl:template>
    <xsl:for-each select="EmployeeTimeSheet/EmployeeTimeSheet">
        <xsl:for-each select="employeeTimeValuationResult/EmployeeTimeValuationResult">
            <xsl:variable name="var_TimeValResult" select="./*"></xsl:variable>
                <EmployeeTimeSheet>
                    <xsl:copy-of select="$var_TimeValResult"/>
                    
                    <xsl:template match="cust_Overtime_HR_ACTION"> 
                    <overtimeHRaction>                  
                        <xsl:value-of select="."/>
                    </overtimeHRaction> 
                    </xsl:template> 
                    
                </EmployeeTimeSheet>
            </xsl:for-each>
        </xsl:for-each>  <!-- EmployeeTimeSheet/EmployeeTimeSheet -->
</EmployeeTimeSheet> 
</xsl:template> 

</xsl:stylesheet>

(apologies for beginner level code)

The output should look like this, with the element cust_Overtime_HR_ACTION for 2024-11-25 added to the EmployeeTimeValuationResult node:

<EmployeeTimeSheet>
<EmployeeTimeSheet>
  <employeeTimeValuationResult>
    <EmployeeTimeValuationResult>
      <bookingDate>2024-11-25T00:00:00.000</bookingDate>
      <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION>
    </EmployeeTimeValuationResult>
  </employeeTimeValuationResult>
</EmployeeTimeSheet>

Can anyone please advise where I'm going wrong? An XSLT test tool just says:

Unable to generate the XML document using the provided XML/XSL input. Errors were reported during stylesheet compilation

I can't figure out the logic required for the retrieval of the corresponding node.

Many thanks in advance for any guidance you can provide.

Kind regards, AJ


Solution

  • Consider this simplified example:

    XML

    <EmployeeTimeSheet>
        <employeeTimeSheetEntry>
            <EmployeeTimeSheetEntry>
                <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION>
                <startDate>2024-11-25T00:00:00.000</startDate>
            </EmployeeTimeSheetEntry>
            <EmployeeTimeSheetEntry>
                <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION>
                <startDate>2024-11-27T00:00:00.000</startDate>
            </EmployeeTimeSheetEntry>
            <EmployeeTimeSheetEntry>
                <cust_Overtime_HR_ACTION>Payout</cust_Overtime_HR_ACTION>
                <startDate>2024-11-30T00:00:00.000</startDate>
            </EmployeeTimeSheetEntry>
        </employeeTimeSheetEntry>
        <employeeTimeValuationResult>
            <EmployeeTimeValuationResult>
                <timeTypeGroup>SCHED_WORK_TIME_A</timeTypeGroup>
                <hours>9</hours>
                <externalCode>ad0a255f256d44ddbe41bc6ab1d632b9</externalCode>
                <entityUUID>6CB3BB8B01C4434FABD28A84554B6A88</entityUUID>
                <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
                <bookingDate>2024-11-25T00:00:00.000</bookingDate>
                <hoursAndMinutes>9:00</hoursAndMinutes>
            </EmployeeTimeValuationResult>
            <EmployeeTimeValuationResult>
                <timeTypeGroup>SCHED_WORK_TIME_A</timeTypeGroup>
                <hours>9</hours>
                <externalCode>ee32afbee0e1452fbdcc21e9ee9e45e5</externalCode>
                <entityUUID>397C8AED082B4B5192941FF6D02C29FA</entityUUID>
                <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
                <bookingDate>2024-11-26T00:00:00.000</bookingDate>
                <hoursAndMinutes>9:00</hoursAndMinutes>
            </EmployeeTimeValuationResult>
            <EmployeeTimeValuationResult>
                <timeTypeGroup>SCHED_WORK_TIME_A</timeTypeGroup>
                <hours>6.5</hours>
                <externalCode>6d84652c40bb49c7b028fda128802100</externalCode>
                <entityUUID>73F775BAF8EA43F3BE011EB0C34601E5</entityUUID>
                <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
                <bookingDate>2024-11-27T00:00:00.000</bookingDate>
                <hoursAndMinutes>6:30</hoursAndMinutes>
            </EmployeeTimeValuationResult>
            <EmployeeTimeValuationResult>
                <timeTypeGroup>SCHED_WORK_TIME_A</timeTypeGroup>
                <hours>9</hours>
                <externalCode>1527b80a2ccf4c409e9549bb22b0da20</externalCode>
                <entityUUID>1397BD090FC0477C8C9EABE8B15939D2</entityUUID>
                <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
                <bookingDate>2024-11-28T00:00:00.000</bookingDate>
                <hoursAndMinutes>9:00</hoursAndMinutes>
            </EmployeeTimeValuationResult>
            <EmployeeTimeValuationResult>
                <timeTypeGroup>TFX_ABSENCES_TTG</timeTypeGroup>
                <hours>2.5</hours>
                <externalCode>e2092ce1a0c6498fadc6d4caa0836ef1</externalCode>
                <entityUUID>A996867E64B9422FBC1765662286B633</entityUUID>
                <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
                <bookingDate>2024-11-27T00:00:00.000</bookingDate>
                <hoursAndMinutes>2:30</hoursAndMinutes>
            </EmployeeTimeValuationResult>
            <EmployeeTimeValuationResult>
                <timeTypeGroup>TFX_ABSENCES_TTG</timeTypeGroup>
                <hours>9</hours>
                <externalCode>50745ba2f6c241fc925a666aef955a50</externalCode>
                <entityUUID>65D732D727E346B79E60318C4703910C</entityUUID>
                <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
                <bookingDate>2024-11-29T00:00:00.000</bookingDate>
                <hoursAndMinutes>9:00</hoursAndMinutes>
            </EmployeeTimeValuationResult>
        </employeeTimeValuationResult>
    </EmployeeTimeSheet>
    

    XSLT 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:strip-space elements="*"/>
    
    <xsl:key name="k1" match="EmployeeTimeSheetEntry" use="startDate" />
    
    <xsl:template match="/EmployeeTimeSheet">
        <results>
            <xsl:for-each select="employeeTimeValuationResult/EmployeeTimeValuationResult">
                <result>
                    <xsl:copy-of select="*"/>
                    <xsl:copy-of select="key('k1', bookingDate)/cust_Overtime_HR_ACTION"/>
                </result>           
            </xsl:for-each>
        </results>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Result

    <?xml version="1.0" encoding="UTF-8"?>
    <results>
       <result>
          <timeTypeGroup>SCHED_WORK_TIME_A</timeTypeGroup>
          <hours>9</hours>
          <externalCode>ad0a255f256d44ddbe41bc6ab1d632b9</externalCode>
          <entityUUID>6CB3BB8B01C4434FABD28A84554B6A88</entityUUID>
          <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
          <bookingDate>2024-11-25T00:00:00.000</bookingDate>
          <hoursAndMinutes>9:00</hoursAndMinutes>
          <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION>
       </result>
       <result>
          <timeTypeGroup>SCHED_WORK_TIME_A</timeTypeGroup>
          <hours>9</hours>
          <externalCode>ee32afbee0e1452fbdcc21e9ee9e45e5</externalCode>
          <entityUUID>397C8AED082B4B5192941FF6D02C29FA</entityUUID>
          <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
          <bookingDate>2024-11-26T00:00:00.000</bookingDate>
          <hoursAndMinutes>9:00</hoursAndMinutes>
       </result>
       <result>
          <timeTypeGroup>SCHED_WORK_TIME_A</timeTypeGroup>
          <hours>6.5</hours>
          <externalCode>6d84652c40bb49c7b028fda128802100</externalCode>
          <entityUUID>73F775BAF8EA43F3BE011EB0C34601E5</entityUUID>
          <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
          <bookingDate>2024-11-27T00:00:00.000</bookingDate>
          <hoursAndMinutes>6:30</hoursAndMinutes>
          <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION>
       </result>
       <result>
          <timeTypeGroup>SCHED_WORK_TIME_A</timeTypeGroup>
          <hours>9</hours>
          <externalCode>1527b80a2ccf4c409e9549bb22b0da20</externalCode>
          <entityUUID>1397BD090FC0477C8C9EABE8B15939D2</entityUUID>
          <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
          <bookingDate>2024-11-28T00:00:00.000</bookingDate>
          <hoursAndMinutes>9:00</hoursAndMinutes>
       </result>
       <result>
          <timeTypeGroup>TFX_ABSENCES_TTG</timeTypeGroup>
          <hours>2.5</hours>
          <externalCode>e2092ce1a0c6498fadc6d4caa0836ef1</externalCode>
          <entityUUID>A996867E64B9422FBC1765662286B633</entityUUID>
          <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
          <bookingDate>2024-11-27T00:00:00.000</bookingDate>
          <hoursAndMinutes>2:30</hoursAndMinutes>
          <cust_Overtime_HR_ACTION>TOIL</cust_Overtime_HR_ACTION>
       </result>
       <result>
          <timeTypeGroup>TFX_ABSENCES_TTG</timeTypeGroup>
          <hours>9</hours>
          <externalCode>50745ba2f6c241fc925a666aef955a50</externalCode>
          <entityUUID>65D732D727E346B79E60318C4703910C</entityUUID>
          <EmployeeTimeSheet_externalCode>dd70365f71084e63b1143d4c40bd40a9</EmployeeTimeSheet_externalCode>
          <bookingDate>2024-11-29T00:00:00.000</bookingDate>
          <hoursAndMinutes>9:00</hoursAndMinutes>
       </result>
    </results>