james corrigan
james corrigan

Reputation: 13

XSL code to change existing xml tag and add new xml tags

I have the following example XML file which contains 1,000's of tags.

<AAA>
 <BBB>
  <CCC>
   <DDD>
    <EEE>
     <1>abc</1>
     <2>abc</2>
     <3>abc</3>
     <4>abc</4>
    </EEE>
   </DDD>
  </CCC>
 </BBB>
</AAA>

I need to transform it to the following (add tag 5 with two sub tags and update tag 1).

<AAA>
 <BBB>
  <CCC>
   <DDD>
    <EEE>
     <1>kkkkkkkk</1>
     <2>abc</2>
     <3>abc</3>
     <4>abc</4>
     <5>
        <a>abc</a>
        <b>abc</b>
     </5>
    </EEE>
   </DDD>
  </CCC>
 </BBB>
</AAA>

I cannot do both things together at all i.e. I can either update tag 1 or add tag 5. I need to be able to do both. I am using the following xsl transform to add tag 5.

<xsl:template match="@* | node()" name="identity-copy">
    <xsl:copy>
        <xsl:apply-templates select="@* | node()"/>
    </xsl:copy> 
</xsl:template> 

 <xsl:template match="e:AAA/e:BBB/e:CCC/e:DDD/e:EEE">   
    <xsl:copy>              
     <xsl:copy-of select="*"/>
      <5>
        <a>abc</a>
        <b>abc</b>
      </5>
    </xsl:copy>     
</xsl:template>

No matter what I try I cannot update tag 1 - the copy-of select="*" seems to be overriding it. Any ideas on how to achieve both the update and addition much appreciated.

Upvotes: 0

Views: 534

Answers (1)

Tomalak
Tomalak

Reputation: 338128

Easy, - just exclude <1> from your copy (XML names can't legally start with a digit, though, so I called it <tag1> and <tag5>):

<xsl:template match="e:AAA/e:BBB/e:CCC/e:DDD/e:EEE">   
  <xsl:copy>
    <tag1>kkkkkkkkkk</tag1>
    <xsl:copy-of select="*[not(self::tag1)]"/>
    <tag5>
      <a>abc</a>
      <b>abc</b>
    </tag5>
  </xsl:copy>
</xsl:template>

The above only really works well when you want to replace the very first element and append a new one at the end, in all other cases it messes up the element order.

Assuming you want to replace <tag2>, using template matching with <xsl:apply-templates> is better, because it keeps input element order:

<xsl:template match="e:AAA/e:BBB/e:CCC/e:DDD/e:EEE">   
  <xsl:copy>
    <xsl:apply-templates select="*" />
    <tag5>
      <a>abc</a>
      <b>abc</b>
    </tag5>
  </xsl:copy>     
</xsl:template>

<xsl:template match="tag2">
    <xsl:copy>zzzzzzzzzz</xsl:copy>
</xsl:template>

The approach with <xsl:apply-templates> is more flexible than the one with <xsl:copy-of>: To insert a new element after <tag3> you could use a template such as:

<xsl:template match="tag3">
    <xsl:copy-of select="." />
    <tag3-1>zzzzzzzzzz</tag3-1>
</xsl:template>

Upvotes: 1

Related Questions