How Does cdata-section-elements Work in XSLT?

In Extensible Stylesheet Language Transformations (XSLT), the cdata-section-elements attribute on the <xsl:output> element instructs the XSLT processor to automatically wrap the text content of specified XML elements inside CDATA sections in the serialized output. This article examines how cdata-section-elements functions, why it is necessary for preserving raw characters like < and &, how to configure multiple elements and namespaces, and key edge cases to consider during transformation.

The Purpose of CDATA Sections in XML

In standard XML serialization, special characters such as the ampersand (&) and the less-than sign (<) are converted into character entities like &amp; and &lt; to prevent the parser from misinterpreting text as markup. While this guarantees valid XML, certain downstream consumers—such as legacy parsers, embedded scripts, or systems consuming raw markup payloads—require character data to be wrapped inside <![CDATA[ ... ]]> blocks without character escaping.

The standard XSLT data model (the XPath tree) does not preserve original CDATA markers from the input XML document. It treats all character data identically as text nodes. Consequently, XSLT requires a serialization-level mechanism to reintroduce CDATA syntax during output generation.

Syntax and Configuration of cdata-section-elements

The cdata-section-elements attribute belongs to the top-level <xsl:output> element. Its value is a whitespace-separated list of QNames (Qualified Names) identifying which output elements should have their text child nodes output inside CDATA sections.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" cdata-section-elements="script query payload"/>

  <xsl:template match="/">
    <root>
      <script>if (a < b && b > 0) { return true; }</script>
      <description>Standard escaping applies here: a &lt; b</description>
    </root>
  </xsl:template>
</xsl:stylesheet>

When serialized, the resulting XML wraps the <script> element's content within CDATA delimiters, while standard elements retain standard entity escaping:

<root>
  <script><![CDATA[if (a < b && b > 0) { return true; }]]></script>
  <description>Standard escaping applies here: a &lt; b</description>
</root>

Handling Namespaces

When targeting elements that belong to an XML namespace, the prefix declared in the stylesheet must be included in the cdata-section-elements list. The processor resolves the QName to its expanded name (URI and local name) to determine if an element matches.

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

  <xsl:output method="xml" cdata-section-elements="app:data"/>

  <xsl:template match="/">
    <app:data>Price: &lt; 50 USD &amp; in stock</app:data>
  </xsl:template>
</xsl:stylesheet>

Output:

<app:data xmlns:app="http://example.com/ns/app"><![CDATA[Price: < 50 USD & in stock]]></app:data>

Serialization Rules and Edge Cases

Serialization Phase Behavior

The attribute does not change the internal node tree created during stylesheet evaluation. It functions strictly during serialization. Attempting to match or query CDATA boundaries within XPath or templates is not supported because CDATA is merely a lexical representation of a text node.

Splitting Closing Sequences

If the text content of a specified element contains the literal string ]]>, XML rules prohibit this sequence inside a single CDATA block. The XSLT serializer automatically handles this by splitting the sequence across adjacent CDATA sections:

<![CDATA[First part of text containing ]]]]><![CDATA[> and continuing]]>

Element Content Only

The cdata-section-elements attribute only applies to element character data. It cannot be used to format attribute values as CDATA, as XML specifications do not allow CDATA blocks within attribute values.