Modern identity and text¶
XSLT 3.0 keeps everything you have learned and adds a handful of conveniences
that quietly delete boilerplate. Two of them stand out for everyday work: a
declaration that gives you the identity transform for free, and text value
templates that let you write {...} directly inside element content. Both
need a 3.0 processor such as Saxon, so the stylesheets here declare
version="3.0".
The identity transform, without the template¶
In Producing XML output the identity transform was a hand-written template — the canonical "copy everything, recurse into attributes and children" rule:
| identity-1.0.xsl | |
|---|---|
XSLT 3.0 lets you ask for that behaviour with a single top-level declaration. The
xsl:mode element configures what happens to a node that no template
matches, and on-no-match="shallow-copy" means copy it — exactly what the
boilerplate above does, recursively, but without writing any template at all.
- This one line replaces the whole
match="@*|node()"identity template. Any node with no explicit template is shallow-copied and its attributes and children are processed, so the whole document flows through unchanged — every node except<price>, which the specific template below rewrites.
The result is identical to the hand-written version in
Producing XML output: every element is copied verbatim, and only
<price> is reshaped — here, rounded to a whole number.
The other on-no-match values¶
shallow-copy is the one that gives you the identity transform, but
on-no-match accepts a whole family of defaults:
deep-copy— copy the node and its entire subtree verbatim, no recursion into templates.shallow-copy— copy the node itself, then process its attributes and children (the identity transform).text-only-copy— emit the node's text content but drop its markup.shallow-skip— emit nothing for the node, but still process its children.deep-skip— emit nothing for the node or its descendants.fail— raise a dynamic error if any node goes unmatched (useful to prove your stylesheet handles every input shape on purpose).
xsl:mode sets defaults for a mode¶
on-no-match is just one setting on xsl:mode. The element is the general place
to configure a mode — including named modes, which you met in
Template modes. A bare <xsl:mode .../> configures the unnamed
default mode; add name="..." to configure one of your own:
| named-mode-defaults.xsl | |
|---|---|
Now xsl:apply-templates with no mode copies nodes through, while
apply-templates select="..." mode="prune" drops unmatched elements but keeps
descending into them — two different default behaviours, one per mode.
Text value templates: {...} in element content¶
You already use {...} in attributes — attribute value templates, as in
<album price="{price}"/>. XSLT 3.0 extends the same syntax to text
content when you switch it on with expand-text="yes".
In 1.0, putting a computed value inside an element means xsl:value-of, and any
literal text around it usually needs xsl:text to control whitespace — see
Whitespace and xsl:text:
| label-1.0.xsl | |
|---|---|
With expand-text="yes" you write the same thing as ordinary text with {...}
holes in it:
| label-3.0.xsl | |
|---|---|
- Set
expand-text="yes"onxsl:stylesheetto enable it everywhere, or on any literal result element to enable it just for that element and its descendants (andexpand-text="no"turns it back off for a subtree). - Each
{...}is an XPath expression evaluated and inserted into the text —{title}and{artist}here. The surrounding characters (Title:, the parentheses, the spaces) are kept exactly as written, so noxsl:textis needed.
Escaping a literal brace
Because { now starts an expression in text content, a brace you want to
appear literally must be doubled: write {{ for a single { and }} for
a single }. For example <code>{{ {name} }}</code> outputs { Bob }
when name is Bob. (This is the same doubling rule attribute value
templates have always used.)
Worked example: copy through, rewrite one element's text¶
Combining both features: a stylesheet that turns on expand-text and uses
on-no-match="shallow-copy". The whole catalog.xml flows through unchanged,
and one template rewrites the text of <title> using a {...} hole — no
xsl:copy boilerplate and no xsl:value-of.
- The free identity transform:
<catalog>,<cd>,<artist>,<price>and every other unmatched node is shallow-copied through unchanged. - The only explicit rule. The new
<title>text is built with two{...}holes —{upper-case(.)}upper-cases the title (a 2.0/3.0 string function), and{../artist}pulls in the sibling artist — joined by literal text.
Applied to the shared catalog.xml, the result keeps the original structure and
only the title text changes:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title>EMPIRE BURLESQUE [Bob Dylan]</title>
<artist>Bob Dylan</artist>
<price>10.90</price>
</cd>
<cd>
<title>HIDE YOUR HEART [Bonnie Tyler]</title>
<artist>Bonnie Tyler</artist>
<price>9.90</price>
</cd>
<cd>
<title>GREATEST HITS [Dolly Parton]</title>
<artist>Dolly Parton</artist>
<price>9.90</price>
</cd>
</catalog>
Note
The two features are independent — you can use expand-text="yes" in a
plain 1.0-style stylesheet structure, or on-no-match="shallow-copy"
without any text value templates. They simply compose well: declarative
"copy everything" plus inline "rewrite this bit of text" is a remarkably
small amount of stylesheet for a precise edit.
Next¶
Reading and writing JSON — the most far-reaching 3.0 addition: parse
JSON into native maps and arrays, navigate it with the ? operator, and
serialise structured data straight back out as JSON.
More to explore¶
You started with the smallest complete 1.0 stylesheet and worked through templates, modes, variables, predicates, string and number handling, whitespace control, reuse, and external documents — then the 2.0/3.0 conveniences and JSON. You now have a working picture of XSLT from its 1.0 fundamentals through its modern form.
XSLT 3.0 has yet more to explore once you need it:
- Higher-order functions — pass functions as values, with
xsl:function, inlinefunction(...) { ... }, and operators likefn:filterandfn:fold-left. - Maps and arrays — first-class associative and ordered data structures
(
map { ... },array { ... }) for building and reshaping structured data. - Streaming — process documents far larger than memory with
xsl:streamand the streamable, window-friendlyxsl:iterate. - Packages — share and version libraries of templates and functions with
xsl:packageandxsl:use-package.
When you want to revisit any earlier topic, head back to the Overview.