#Output Methods
One of XSLT's greatest strengths is producing multiple output formats from the same source data. The xsl:output declaration controls how the transformation result is serialized.
#Contents
#HTML Output
The most common output for web-facing transforms:
<xsl:output method="html" html-version="5" indent="yes" encoding="UTF-8"/>What HTML mode does:
-
Outputs
<!DOCTYPE html>for HTML5 -
Self-closes void elements correctly (
<br>,<img>,<meta>, not<br/>) -
Does not escape
<script>and<style>content -
Includes
<meta charset="UTF-8">when requested -
Writes boolean attributes correctly (
<input disabled>not<input disabled="disabled">)
<xsl:template match="/">
<html>
<head>
<meta charset="UTF-8"/>
<title><xsl:value-of select="//title"/></title>
</head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>Output:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>My Page</title>
</head>
<body>...</body>
</html>#XHTML Output
For XML-compatible HTML:
<xsl:output method="xhtml" indent="yes" encoding="UTF-8"/>Produces well-formed XML that is also valid HTML — <br/>, <meta ... />, etc.
#XML Output
The default output method. Produces well-formed XML.
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="no"/>Use cases:
-
Transforming one XML format to another (e.g., data conversion between standards)
-
Generating SVG, MathML, RSS/Atom feeds
-
Producing intermediate XML for further processing
Example — converting a product catalog to an Atom feed:
<xsl:output method="xml" indent="yes"/>
<xsl:template match="catalog">
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Product Updates</title>
<link href="https://example.com/products"/>
<updated><xsl:value-of select="current-dateTime()"/></updated>
<xsl:apply-templates select="product"/>
</feed>
</xsl:template>
<xsl:template match="product">
<entry xmlns="http://www.w3.org/2005/Atom">
<title><xsl:value-of select="name"/></title>
<id>urn:product:<xsl:value-of select="@id"/></id>
<summary><xsl:value-of select="description"/></summary>
</entry>
</xsl:template>#Text Output
Produces plain text with no markup:
<xsl:output method="text" encoding="UTF-8"/>What text mode does:
-
No XML/HTML escaping —
<is output as<, not< -
No XML declaration
-
Only text nodes are output (element tags are suppressed)
-
Whitespace from the stylesheet is preserved (be careful!)
#Example: Generating a CSV File
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/">
<xsl:text>ID,Name,Price,Category </xsl:text>
<xsl:for-each select="//product">
<xsl:value-of select="@id"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="name"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="price"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="@category"/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>Output:
ID,Name,Price,Category
P001,Wireless Mouse,29.99,electronics
P002,Mechanical Keyboard,89.99,electronics
P003,USB-C Hub,45.00,accessories#Example: Generating a Markdown File
<xsl:output method="text"/>
<xsl:template match="catalog">
<xsl:text># Product Catalog </xsl:text>
<xsl:apply-templates select="product"/>
</xsl:template>
<xsl:template match="product">
<xsl:text>## </xsl:text>
<xsl:value-of select="name"/>
<xsl:text> </xsl:text>
<xsl:text>**Price:** $</xsl:text>
<xsl:value-of select="price"/>
<xsl:text> </xsl:text>
<xsl:value-of select="description"/>
<xsl:text> </xsl:text>
</xsl:template>#Example: Generating SQL
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:for-each select="//product">
<xsl:text>INSERT INTO products (id, name, price, category) VALUES (</xsl:text>
<xsl:value-of select="concat('''', @id, ''', ')"/>
<xsl:value-of select="concat('''', name, ''', ')"/>
<xsl:value-of select="concat(price, ', ')"/>
<xsl:value-of select="concat('''', @category, '''')"/>
<xsl:text>); </xsl:text>
</xsl:for-each>
</xsl:template>#JSON Output
XSLT 3.0 can produce JSON directly. The approach uses XPath maps and arrays:
<xsl:output method="json" indent="yes"/>
<xsl:template match="catalog">
<xsl:sequence select="
map {
'products': array {
for $p in product
return map {
'id': string($p/@id),
'name': string($p/name),
'price': number($p/price),
'category': string($p/@category)
}
}
}
"/>
</xsl:template>Output:
{
"products": [
{
"id": "P001",
"name": "Wireless Mouse",
"price": 29.99,
"category": "electronics"
},
...
]
}
C# comparison: This is like building a Dictionary<string, object> and calling JsonSerializer.Serialize() — but the data selection and structure are expressed in XPath.
Key insight for JSON developers: You're already comfortable with JSON. XSLT 3.0 lets you use XPath to query XML and output JSON — bridging the two formats. This is one of the most compelling use cases for .NET developers doing data integration.
#CSV Output
PhoenixmlDb supports method="csv" as a built-in output method for producing comma-separated values directly, without manually building CSV with method="text":
<xsl:output method="csv" encoding="UTF-8"/>
<xsl:template match="catalog">
<!-- Header row -->
<csv>
<row>
<field>ID</field>
<field>Name</field>
<field>Price</field>
<field>Category</field>
</row>
<xsl:for-each select="product">
<row>
<field><xsl:value-of select="@id"/></field>
<field><xsl:value-of select="name"/></field>
<field><xsl:value-of select="price"/></field>
<field><xsl:value-of select="@category"/></field>
</row>
</xsl:for-each>
</csv>
</xsl:template>Output:
ID,Name,Price,Category
P001,Wireless Mouse,29.99,electronics
P002,Mechanical Keyboard,89.99,electronics
P003,USB-C Hub,45.00,accessoriesThe CSV output method handles quoting of fields that contain commas, double quotes, or newlines automatically. For more control over CSV formatting with method="text", see the Text Output section above.
#Adaptive Output
XSLT 3.0's adaptive output method automatically chooses the serialization based on the result type:
<xsl:output method="adaptive"/>-
If the result is a document node → serialized as XML
-
If the result is a map or array → serialized as JSON
-
If the result is an atomic value → serialized as text
Useful for functions and transforms that may return different types.
#Multiple Outputs in One Stylesheet
Using xsl:result-document, a single stylesheet can produce multiple files with different formats:
<xsl:template match="catalog">
<!-- Main HTML page -->
<html>
<body><xsl:apply-templates select="product"/></body>
</html>
<!-- JSON API response -->
<xsl:result-document href="api/products.json" method="json" indent="yes">
<xsl:sequence select="array { for $p in product return map { ... } }"/>
</xsl:result-document>
<!-- CSV export -->
<xsl:result-document href="export/products.csv" method="text">
<xsl:text>id,name,price </xsl:text>
<xsl:for-each select="product">
<xsl:value-of select="string-join((@id, name, price), ',')"/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:result-document>
<!-- Sitemap -->
<xsl:result-document href="sitemap.xml" method="xml" indent="yes">
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<xsl:for-each select="product">
<url><loc>https://example.com/products/<xsl:value-of select="@id"/></loc></url>
</xsl:for-each>
</urlset>
</xsl:result-document>
</xsl:template>One XSLT, four output files in three formats. This is the kind of thing that takes significant code in C# but is natural in XSLT.
#Serialization Parameters
The full set of xsl:output attributes:
|
Attribute |
Values |
Default |
Description |
|---|---|---|---|
|
|
|
|
Output format |
|
|
|
HTML version (use |
|
|
|
|
|
Pretty-print the output (XML, HTML, XHTML) |
|
|
|
|
Character encoding |
|
|
|
|
Include |
|
|
|
|
XML standalone declaration |
|
|
URI |
System DOCTYPE identifier |
|
|
|
String |
Public DOCTYPE identifier |
|
|
|
Space-separated names |
Elements to output as CDATA sections |
|
|
|
Space-separated element names |
Elements whose content should not be indented even when |
|
|
|
|
|
Whether to emit a BOM at the start of the output |
|
|
|
|
Whether to percent-encode non-ASCII characters in URI-valued attributes |
|
|
Method name |
|
Serialization method for nodes within JSON output (used when JSON output contains XML nodes) |
|
|
MIME type |
Output MIME type hint |
|
|
|
|
|
Include |
|
|
String |
Separator between sequence items (text mode) |
#DOCTYPE Declarations
Use doctype-public and doctype-system to produce DOCTYPE declarations in the output:
<!-- HTML 4.01 Transitional -->
<xsl:output method="html"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"
doctype-system="http://www.w3.org/TR/html4/loose.dtd"/>
<!-- XHTML 1.0 Strict -->
<xsl:output method="xhtml"
doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/>For HTML5, use html-version="5" instead, which outputs <!DOCTYPE html> automatically.
#Suppress Indentation
When indent="yes" is set, some elements should not have their content reformatted (for example, <pre> or <code> blocks). Use suppress-indentation to list those elements:
<xsl:output method="html" html-version="5" indent="yes"
suppress-indentation="pre code textarea"/>#Byte Order Mark
Some systems require a BOM (byte order mark) at the beginning of the file:
<xsl:output method="xml" encoding="UTF-8" byte-order-mark="yes"/>#JSON Node Output Method
When producing JSON output that may contain XML nodes (for example, mixed content), json-node-output-method controls how those nodes are serialized within the JSON string:
<xsl:output method="json" indent="yes" json-node-output-method="html"/>#Named Output Definitions
You can define multiple named outputs and reference them from xsl:result-document:
<xsl:output name="html-output" method="html" html-version="5" indent="yes"/>
<xsl:output name="json-output" method="json" indent="yes"/>
<xsl:result-document href="page.html" format="html-output">
...
</xsl:result-document>
<xsl:result-document href="data.json" format="json-output">
...
</xsl:result-document>