score:0

Are you looking for something like this:

 <xsl:template match="Tag">
    <xsl:element name="Genre">
      <xsl:value-of select="@Name"/>           
    </xsl:element>    
  </xsl:template>

score:1

You can match any node with node(), like this:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<!-- Uncomment to remove Tags elements -->
<!-- <xsl:template match="Tags" /> -->

<xsl:template match="Genres">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()" />
        <xsl:for-each select="../Tags/Tag">
            <xsl:element name="Genre">
                <xsl:value-of select="@Name" />
            </xsl:element>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

<!-- Default rule: Copy node and descend -->
<xsl:template match="@*|node()">
        <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
</xsl:template>

</xsl:stylesheet>

score:2

Using one of the most fundamental and powerful XSLT design patterns: overriding the identity template, one will write this very simple transformation to replace every "Genres" element with a "Topics" element:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output omit-xml-declaration="yes"/>

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

    <xsl:template match="Genres">
      <Topics>
         <xsl:apply-templates select="node()|@*"/>
      </Topics>
    </xsl:template>
</xsl:stylesheet>

When applied against the provided source XML document:

<Collection>
    <DVD>
        <ID>0000502461</ID>
        <Title>Cirque du Soleil: Alegría</Title>
        <Released>2002-05-31</Released>
        <RunningTime>90</RunningTime>
        <Genres>
            <Genre>Family</Genre>
            <Genre>Music</Genre>
        </Genres>
        <Overview>What if anything were possible? What if ...    </Overview>
        <Notes/>
        <Tags>
            <Tag Name="Kids" FullName="Kids"/>
        </Tags>
    </DVD>
</Collection>

The wanted result is produced:

<Collection>
    <DVD>
        <ID>0000502461</ID>
        <Title>Cirque du Soleil: Alegría</Title>
        <Released>2002-05-31</Released>
        <RunningTime>90</RunningTime>
        <Topics>
            <Genre>Family</Genre>
            <Genre>Music</Genre>
        </Topics>
        <Overview>What if anything were possible? What if ...    </Overview>
        <Notes/>
        <Tags>
            <Tag Name="Kids" FullName="Kids"/>
        </Tags>
    </DVD>
</Collection>

The first template in the stylesheet is the identity rule. It copies any matched node unchanged and recursively applies templates to its attributes or children. If no other template is present, this template creates identical copy of the source xml document, hence its name.

When there is a more specific template (specifying more specific details for the matched node, such as name and/or other conditions), it is said to "override" the more generic templates. This means that the more specific template is chosen for processing the node.

Using this extremely powerful design pattern it is trivial to implementin just a few lines such processing as:

  1. Delete all nodes that satisfy some condition.
  2. Rename all nodes that satisfy some condition.
  3. Modify the contents of all nodes that satisfy some condition

while copying all other nodes intact.

In our case, the second template is more specific and it gets selected for processing of every element named "Genres". All it does is create an element named "Topics" and inside it apply templates to all of the current node attributes and children.

Finally, the following transformation will add a new "Genre" element to the children of "Genres", for each "Tag" element:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output omit-xml-declaration="yes"/>

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

    <xsl:template match="Genres">
      <xsl:copy>
         <xsl:apply-templates select="node()|@*"/>
         <xsl:apply-templates select="../Tags/Tag" mode="Gen"/>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="Tag" mode="Gen">
      <Genre>
        <xsl:value-of select="@Name"/>
      </Genre>
    </xsl:template>
</xsl:stylesheet>

The result is again as required:

<Collection>
    <DVD>
        <ID>0000502461</ID>
        <Title>Cirque du Soleil: Alegría</Title>
        <Released>2002-05-31</Released>
        <RunningTime>90</RunningTime>
        <Genres>
            <Genre>Family</Genre>
            <Genre>Music</Genre>
        <Genre>Kids</Genre>
        </Genres>
        <Overview>What if anything were possible? What if ...    </Overview>
        <Notes/>
        <Tags>
            <Tag Name="Kids" FullName="Kids"/>
        </Tags>
    </DVD>
</Collection>

More code snippets using the "identity rule" pattern can be found here.

score:0

I had the same need in VB9 and wanted to go with Linq instead of XSL. I found this: http://msdn.microsoft.com/en-us/library/bb669154.aspx and it worked really well.


Related Articles