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
& and < 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 < 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 < 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: < 50 USD & 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.