xsl:copy

1. xsl:copy question
2. xsl:copy or xsl:copy-of
3. Partial Copy
4. Copying only non-empty attributes

1.

xsl:copy question

Nikolai Grigoriev


Suppose I have the following XML:

<employee FullSecurity="0">
  <name>John Doe</name>
  <department>Widget Sales</department>
  <phone>(555)555-5555<
extension>2974</extension></phone>
  <salary>62,000</salary>
  <area>3</area>
</employee>

I want to create a very simple XSL stylesheet which does the
following: If the FullSecurity attribute evaluates to
"true", then copy all of the source tree to the
result tree.  If the FullSecurity attribute evaluates to
"false", copy all of the source tree *except for the
<salary> element* to the result tree.


My solution is 4 lines in 2 templates (not counting the comments):

<!-- By default, copy a node with all the 
        children through. -->
<!-- Attributes are dropped; to include them, 
        use match="*|@*" -->

<xsl:template match="*">
  <xsl:copy> <xsl:apply-templates/> </xsl:copy>
</xsl:template>

<!-- Processing of salary nodes is disabled for 
   FullSecurity="0" -->
<xsl:template match="salary[/employee/@FullSecurity=0]"/>

            

2.

xsl:copy or xsl:copy-of

Mike Kay +

general principle: xsl:copy-of for a deep copy, xsl:copy for a shallow copy, xsl:apply-templates for a transformation.

I.e. xsl:copy copies the current node, sub-elements are not copied xsl:copy-of copies nodes and all their descendants.

3.

Partial Copy

Oliver Becker



> I want to write an xsl that will
> basically copy an xml tree, but allow me to chnage the values of certain
> nodes. For example:
> 
> <highlevel>Highest Level
> <innernode>This is an innernode
> <lownode>This is a low-level node</lownode>
> </innernode>
> </highlevel>
> 
> I want to copy this, but change the text of <innernode>, such as 
> 
> <highlevel>Highest Level
> <innernode>I have edited this text
> <lownode>This is a low-level node</lownode>
> </innernode>
> </highlevel>
> 
    

You shouldn't use xsl:copy-of. This makes a deep copy without processing the inner elements. Use xsl:copy and xsl:apply-templates - see the identity transformation in the XSLT spec http://www.w3.org/TR/xslt#copying

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

Now you can add special templates for special element nodes, such as innernode. But note: to process lownode you should call xsl:apply-templates as well. So your template becomes

<xsl:template match="innernode">
	<innernode>
	I have edited this node
	<xsl:apply-templates select="*" />  
     <!-- only elements, no text nodes -->
	</innernode>
</xsl:template>

I haven't tested it, but it should work.

4.

Copying only non-empty attributes

David Carlisle

xsl:copy-of select="@*" is the standard way of copying all attributes.

However You can simplify

<xsl:template match="PERSNAME">
<xsl:element name="PERSNAME">

to either

<xsl:template match="PERSNAME">
<xsl:copy>

or

<xsl:template match="PERSNAME">
<PERSNAME>

and you can simplify

<xsl:copy-of select="@*"/>
<xsl:copy-of select="node()"/>

to

<xsl:copy-of select="@*|node()"/>

actually if that was exactly what you wanted, you are just copying an entire element, so you could further simplify to

<xsl:template match="PERSNAME">
<xsl:copy-of select="."/>
</xsl:template>

> Also it would be nice not to have the empty attributes 

so

 <xsl:template match="PERSNAME">
   <PERSNAME>
   <xsl:copy-of select="@*[not(.='')]|node()"/>
   </PERSNAME>
 </xsl:template>