How to Strip Elements with an XSLT Identity Transform?

Modifying an Extensible Stylesheet Language Transformations (XSLT) identity transform is the standard technique for removing specific tags from an XML file while preserving the remaining document structure, attributes, and text intact. By combining the baseline identity rule with empty matching templates, XSLT processors discard targeted nodes and suppress their output entirely without requiring you to manually define rules for every retained element.

Understanding the Identity Transformation

The foundation of this approach is the identity template, which recursively copies every node and attribute from the source document directly to the output stream.

In XSLT 1.0 and 2.0, the standard identity rule is written as:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <!-- Copy all nodes and attributes -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

In XSLT 3.0, the built-in identity mode simplifies this rule into a single declaration:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>

Discarding Specific Elements

Because specific templates override more general templates due to standard XSLT priority rules, you can delete an element by declaring an empty template that matches that element’s name. When the processor encounters the targeted element, it invokes the empty rule, produces no output, and stops processing that element's child nodes.

<!-- Completely removes <unwantedElement> and all its children -->
<xsl:template match="unwantedElement"/>

To remove multiple distinct elements in a single stylesheet, join their names using the XPath union operator (|):

<!-- Removes multiple specified elements -->
<xsl:template match="tempData | internalComment | draftNotes"/>

Removing Elements While Preserving Content

In scenarios where the wrapping XML tags should be discarded but the internal text or child elements must remain in the final document, configure the template to apply child templates rather than leaving the body empty:

<!-- Strips the <wrapper> tag but keeps its child text and elements -->
<xsl:template match="wrapper">
  <xsl:apply-templates select="@* | node()"/>
</xsl:template>

Full Example

Given the following source XML:

<catalog>
  <book id="101">
    <title>Advanced XML Processing</title>
    <internalNotes>Internal review only.</internalNotes>
    <price currency="USD">45.00</price>
  </book>
</catalog>

The modified stylesheet removes the <internalNotes> element while copying all other nodes:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

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

  <!-- Strip targeted element -->
  <xsl:template match="internalNotes"/>
</xsl:stylesheet>

The resulting XML output preserves the catalog hierarchy without the suppressed node:

<catalog>
  <book id="101">
    <title>Advanced XML Processing</title>
    <price currency="USD">45.00</price>
  </book>
</catalog>