testermaster
testermaster

Reputation: 1065

Is there any difference between <xsl:copy> and <xsl:element name="{name()}">?

I'd like to know if there's any difference between:

<xsl:template match="/*">
  <xsl:element name="{name()}">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

and:

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

Upvotes: 1

Views: 553

Answers (3)

Mathias M&#252;ller
Mathias M&#252;ller

Reputation: 22617

The xsl:element element generates a new element node.

xsl:copy copies a context item to the output sequence. However, the said context item does not have to be an element node. In your case:

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

The context item is indeed an element. This has the following consequences:

  • xsl:copy also copies the element's namespaces if any
  • an optional sequence constructor inside xsl:copy is used if the context item is an element. In your example, the sequence constructor is <xsl:apply-templates/>

Summary: xsl:copy can copy any kind of node (atomic value, document, element, text, attribute, processing instruction, comment, namespace) whereas xsl:element is solely capable of generating element nodes.

Usage: Use xsl:copy to effectively copy nodes from input XML to the output sequence. Use xsl:element only if element names are dynamic, that is, not known beforehand.

As an aside note, with XSLT 2.0 you can control the treatment of namespaces explicitly with the attributes copy-namespaces and inherit-namespaces.

Upvotes: 1

michael.hor257k
michael.hor257k

Reputation: 116959

There is at least one difference. Given the following input:

<root xmlns="htpp://www.example.com/my">my text</root>

this template:

<xsl:template match="/*">
  <xsl:element name="{name()}">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

will produce:

<root>my text</root>

while xsl:copy will copy the element with its namespace preserved:

<root xmlns="htpp://www.example.com/my">my text</root>

EDIT
In response to comment by @parakmiakos:

Given the following input:

<my:root xmlns:my="htpp://www.example.com/my">my text</my:root>

this template*:

<xsl:template match="/*">
  <xsl:element name="{name()}">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

will produce:

<my:root xmlns:my="htpp://www.example.com/my">my text</my:root>

(*) provided that "my" prefix is declared in the stylesheet.

This template:

<xsl:template match="/*">
  <xsl:element name="{local-name()}">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

will produce:

<root>my text</root>

and this one:

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

will return:

<my:root xmlns:my="htpp://www.example.com/my">my text</my:root>

same as the first one (but without having to declare the prefix).

Upvotes: 2

Ian Roberts
Ian Roberts

Reputation: 122364

The difference is apparent when you have namespace declarations in scope (other than the namespace of the element that you are copying). Using <xsl:copy> will copy any in-scope namespace nodes, using <xsl:element> won't. So given an input file of

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <foo/>
</root>

a stylesheet of

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="/">
    <xsl:apply-templates select="*/*" />
  </xsl:template>

  <xsl:template match="foo">
    <xsl:copy/>
  </xsl:template>
</xsl:stylesheet>

you will get output of

<?xml version="1.0"?>
<foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />

because <xsl:copy> has copied the in-scope namespace nodes from the input element. Whereas if you use

  <xsl:template match="foo">
    <xsl:element name="{name()}" namespace="{namespace-uri()}"/>
  </xsl:template>

you will get

<?xml version="1.0"?>
<foo/>

As Matthias points out, in XSLT 2.0 you can say <xsl:copy copy-namespaces="no"> to suppress the copying of in-scope namespaces - for an element node, <xsl:copy copy-namespaces="no"> has the same effect as <xsl:element name="{name()}" namespace="{namespace-uri()}">

Upvotes: 1

Related Questions