'Add new child element using xsl 3.0

I need to transform following xml as Expected Output using xsl 3.0 or xsl 2.0. Need to add new element called <key> </key> for each <row> element by combining its legacyID and company.

Input XML:

<AggregatedData>
   <Data>
      <Entry>
         <legacyID>ABC</legacyID>
         <AssociateID>123</AssociateID>
      </Entry>
      <Entry>
         <legacyID>CDE</legacyID>
         <AssociateID>456</AssociateID>
      </Entry>
   </Data>
   <root>
      <row>
         <legacyID>ABC</legacyID>
         <company>Test Company 1</company>
         <firstname>Test1</firstname>
      </row>
      <row>
         <legacyID>CDE</legacyID>
         <company>Test Company 2</company>
         <firstname>Test2</firstname>
      </row>
   </root>
</AggregatedData>

Expected Output:

<AggregatedData>
   <Data>
      <Entry>
         <legacyID>ABC<legacyID>
         <AssociateID>123<AssociateID>
      <Entry>
      <Entry>
         <legacyID>CDE</legacyID>
         <AssociateID>456</AssociateID>
      </Entry>
   </Data>
   <root>
      <row>
         <key>ABC_Test Company 1</key>
         <legacyID>ABC</legacyID>
         <company>Test Company 1</company>
         <firstname>Test1</firstname>
      </row>
      <row>
         <key>ABC_Test Company 2</key>
         <legacyID>CDE</legacyID>
         <company>Test Company 2</company>
         <firstname>Test2</firstname>
      </row>
   </root>
</AggregatedData>

I used following xsl code,

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="yes" />
   <xsl:template match="root/*">
      <xsl:copy>
         <xsl:element name="key">
            <xsl:value-of select="concat(//root/row/legacyID,' ',/row/company)" />
         </xsl:element>
         <xsl:call-template name="copy-children" />
      </xsl:copy>
   </xsl:template>
   <!-- Copy the children of the current node. -->
   <xsl:template name="copy-children">
      <xsl:copy-of select="./*" />
   </xsl:template>
   <!-- Generic identity template -->
   <xsl:template match="node()|@*">
      <xsl:copy>
         <xsl:apply-templates select="@*" />
         <xsl:apply-templates />
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

But this gave the output like below,

<?xml version="1.0" encoding="UTF-8"?>
<AggregatedData>
   <Data>
      <Entry>
         <legacyID>ABC</legacyID>
         <AssociateID>123</AssociateID>
      </Entry>
      <Entry>
         <legacyID>CDE</legacyID>
         <AssociateID>456</AssociateID>
      </Entry>
   </Data>
   <root>
      <row>
         <key>ABC_Test Company 1</key>
         <legacyID>ABC</legacyID>
         <company>Test Company 1</company>
         <firstname>Test1</firstname>
      </row>
      <row>
         <key>ABC_Test Company 1</key>
         <legacyID>CDE</legacyID>
         <company>Test Company 2</company>
         <firstname>Test2</firstname>
      </row>
   </root>
</AggregatedData>

According to above output both row values updated with the same key value. I need to correct that and need to use xsl 2.0.



Solution 1:[1]

In XSLT 3.0 you could reduce the code to:

<xsl:stylesheet version="3.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
expand-text="yes">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

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

<xsl:template match="row/legacyID">
    <key>{.}_{../company}</key>
    <xsl:copy-of select="."/>
</xsl:template>
  
</xsl:stylesheet>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1