tags:

views:

53

answers:

1

Hi. I've found other questions on moving nodes upwards into the parent but I'm missing the trick to move them down into a newly created node.

Given:

<Villain>
  <Name>Dr Evil</Name>
  <Age>49</Age>
  <Like>Money</Like>
  <Like>Sharks</Like>
  <Like>Lasers</Like>
</Villain>

I'm trying to transform this with XSLT to:

<Villain>
  <Name>Dr Evil</Name>
  <Age>49</Age>
  <Likes>
    <Like>Money</Like>
    <Like>Sharks</Like>
    <Like>Lasers</Like>
  </Likes>
</Villain>

In other words, insert a new child node and move all the child nodes called "Like" under it.

+5  A: 

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

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

  <xsl:template match="Like[1]">
   <Likes>
    <xsl:apply-templates select="../Like" mode="copy"/>
   </Likes>
  </xsl:template>

   <xsl:template match="*" mode="copy">
    <xsl:call-template name="identity"/>
   </xsl:template>
   <xsl:template match="Like"/>
 </xsl:stylesheet>

when applied on the provided XML document:

<Villain>
  <Name>Dr Evil</Name>
  <Age>49</Age>
  <Like>Money</Like>
  <Like>Sharks</Like>
  <Like>Lasers</Like>
</Villain>

produces the wanted, correct result:

<Villain>
   <Name>Dr Evil</Name>
   <Age>49</Age>
   <Likes>
      <Like>Money</Like>
      <Like>Sharks</Like>
      <Like>Lasers</Like>
   </Likes>
</Villain>

Do note:

  1. The use and overriding of the identity rule.

  2. The use of modes to specify a somewhat different processing.

Dimitre Novatchev
Worked great, thank you! For anyone else using this, you need to have name="identity" in your identity template rule (not shown).
DJC
@DJC: Sorry, I had an issue with the code formatting. Fixed now.
Dimitre Novatchev
@Dimitre: +1 Nice answer!
Alejandro
@Dimitre, I was going to say, I like the fact that you put `name="identity"` on the identity template. It makes the code more readeble. I hadn't seen that before. ... But now I see @DJC's comment that implies that `name="identity"` is required. Huh? I've never known it to be required.
LarsH
@LarsH: generally identity template doesn't require `name="identity`. Probably you just accidentally forgot that in Dimitre's code you call this template by name and I believe that this is the only reason for @DJC's comment. Nevertheless, nice question and answer +1
jasso
@jasso: thanks, now I see that Dimitre is calling it by name from within the mode="copy" template. (And here I thought he was just being a "literate programmer." :-)) I was thinking only of the other way that the identity template is being called/applied, i.e. from the default template, for which it doesn't need a name.
LarsH
@LarsH, @jasso: Thanks, guys. This is an interesting example how call-template and apply-templates can be used together, isn't it? :)
Dimitre Novatchev