Maintained by: David J. Birnbaum (djbpitt@gmail.com) 
            ![Creative Commons BY-NC-SA 3.0 Unported License [Creative Commons BY-NC-SA 3.0 Unported License]](http://www.obdurodon.org/images/cc/88x31.png) Last modified:
        
        2021-12-27T22:03:57+0000
        
    
    Last modified:
        
        2021-12-27T22:03:57+0000
This page aims to provide a brief introduction to small but useful enhancements to XPath and XSLT that have emerged since the publication of Michael Kay’s XSLT 2.0 and XPath 2.0 programmer’s reference, 4th edition, which covers XPath 2.0 and XSLT 2.0. Two of the most significant additions to XSLT 3.0, streaming and packaging, are not covered here because, as important as they are for large files or complex transformations, we haven’t found a need for them in the smaller scale on which we usually operate.
To tell <oXygen/> that new XSLT files should default to XSLT 3.0, click on File
                → New → XSLT → Customize and select 3.0
.
XPath in XSLT allows the use of the let construction, which was
                    previously available only in XQuery. See immediately below, under Concatenation.
||The string concatenation operator || can be used in situations that
                    previously required the concat() function. For example, the
                    following XPath expression:
let $a := 'hi', $b := 'bye' return $a || ' ' || $b
                is equivalent to:
let $a := 'hi',  $b := 'bye' return concat($a,' ', $b)
            !) The bang operator applies the operation to the right of the bang to each item in the sequence on the left. For example:
('curly', 'larry', 'moe') ! string-length(.)
                returns a sequence of three integers: (5, 5, 3). The expression is equivalent to:
for $stooge in ('curly', 'larry', 'moe') return string-length($stooge)
The simple mapping operator is similar to /, except that 1) the
                    sequence to the left of / must be a sequence of nodes, while the
                    sequence to the left of ! can be a sequence of any items, and 2)
                        / sorts the sequence on the left into document order and
                    eliminates duplicates, while ! performs no sorting or
                    deduplication.
=>)The arrow operator pipes the output of the item on the left into the first argument of the function on the right. It thus provides an alternative to nested parentheses. For example (from the XPath 3.1 spec, §3.16):
tokenize((normalize-unicode(upper-case($string))),"\s+")
is equivalent to:
$string => upper-case() => normalize-unicode() => tokenize("\s+")
                The functionality of the bang and arrow operators overlaps where the operation on the right is a function, but only then. For that reason:
$book ! (@author, @title)
return the values of the @author and @title attributes
                    of some element that is the value of the variable $book, but
                    because the operation on the right is not function, if you replace the bang with
                    the arrow operator, you throw an error. The arrow operator does not use the dot
                    to specify the first argument to the function because the operator supplies that
                    argument instead.
Because the bang operator is a mapping and the arrow operator is a pipe, the following two expressions produce different results:
'curly larry moe' => tokenize('\s+') => count()
                The preceding returns the integer value 3
. But
'curly larry moe' ! tokenize(.,'\s+') ! count(.)
returns a sequence of three instances of the integer value 1
. The
                    difference is that after tokenize() returns a sequence of three
                    items, the bang operator maps each item individually as the input to the
                        count() function, while the arrow operator counts the items in
                    the sequence.
unparsed-text-lines()
                unparsed-text-lines() works like unparsed-text(),
                    except that it tokenizes on newlines and streams the input line by line.
The following example creates a map and then serializes it as JSON on output:
<xsl:variable name="mymap" as="map(*)"
        select='map {
        "Su" : "Sunday",
        "Mo" : "Monday",
        "Tu" : "Tuesday",
        "We" : "Wednesday",
        "Th" : "Thursday",
        "Fr" : "Friday",
        "Sa" : "Saturday"
        }'/>
    <xsl:template match="/">
        <root>
            <text>Hi, Mom! Here’s some information:</text>
            <para>{
                serialize($mymap, map{"method":"json","indent":true()})
            }</para>
        </root>
    </xsl:template>
                $stuff?row ...
In eXist-db, to create a map using a for loop use something
                    like:
declare variable $map as map(*) := 
    map:merge(for $i in $realTitles return map:entry($i, count($items/tei:title[. eq $i])));
                The map:entry() function creates anonymous separate one-item maps
                    with the string values of $realTitles as the keys and the number of
                    times each title appears in the corpus as the value. Wrapping the FLWOR in the
                        map:merge() function merges the individual maps into a single
                    map, which is assigned to the variable $map. Note the syntax of the
                    value specified by the as operator, which is necessary (should we
                    choose to specify a datatype) because maps are not traditional atomic types. To
                    access the values of the map, use something like:
for $bg in map:keys($map)
let $en as element(en) := $titles[bg eq $bg]/en
order by $en
return <option value="{$bg}">{$en || ' (' || $map($bg) || ')'}</option>
                This gets each key from the map, uses it to retrieve the English translation of a
                    Bulgarian title from the $titles variable, and then also uses it as
                    the single argument of the $map() function to retrieve the number
                    of times the Bulgarian title appears in the corpus. Note that because maps are
                    functions, instead of indexing into them with square brackets, we execute them
                    with the key as the single argument to the function, and the argument is in
                    parentheses, as is usual for functions.
Add stuff here
Boolean values can be expressed as any of true
/1
/yes
 or
                        false
/0
/no
. For example, to turn on pretty-printed
                    output, set the value of the @indent attribute of
                        <xsl:output> to any of true
, 1
, or
                        yes
.
If you set the value of the @name attribute of an
                        <xsl:template> element to xsl:initial-template
 and
                    run a transformation from the command line with the -it
 (= ‘initial
                    template’) switch, the template named xsl:initial-template
 is now the
                    default. Previously you had to specify the name of your initial template on the
                    command line.
Like Attribute Value Templates, Content Value Templates let you specify that
                    certain text should be intepreted as XPath instead of being output literally.
                    The syntax for CVTs is the same as for AVTs: surround the expression in curly
                    braces (to use a literal curly brace, double them), and multiple values are
                    output with a single space between them. CVTs work ony if you create an
                        @expand-text attribute on the root
                        <xsl:stylesheet> element and give it a positive Boolean
                    value. CVTs are similar to the use of curly braces in XQuery to switch from XML
                    mode into XQuery mode, and they can be used in situations where you may
                    previously have had to use <xsl:value-of> or something that
                    converts its arguments to strings, like concat() or
                    ||. Here’s an example:
<xsl:template name="xsl:initial-template">Hello, World! It’s {current-time()}</xsl:template>
                The preceding is equivalent to:
<xsl:template name="xsl:initial-template">
    <xsl:text>Hello, World! It’s </xsl:text>
    <xsl:value-of select="current-time()"/>
</xsl:template>
                or
<xsl:template name="xsl:initial-template">
    <xsl:value-of select="concat('Hello, World! It’s ', current-time())"/>
</xsl:template>
                or
<xsl:template match="/">
    <xsl:value-of select="'Hello, World! It’s ' || current-time()"/>
</xsl:template>
            @item-separator
                The @item-separator attribute on <xsl:output> can
                    be used to change the item separator from the default space to something else.
                    Must be combined with @build-tree="no".
Shadow attributes mask regular attribute values, and have the same name as the regular attribute, but with a leading underscore.
Functions can be assigned to a variable. To reference them, add parentheses after the variable name.
If you serve your HTML5 as mime type application/xhtml+xml
 and want to
                    validate it as XML, use:
<xsl:output method="xhtml" version="5.0" omit-xml-declaration="no" include-content-type="no"/>
To create HTML5 (not XHTML) output, use
<xsl:output method="html" version="5"/>
This omits the XML declaration and creates a <meta> element
                    inside the <head>.
The identity transformation can be expressed in a single top-level
                        <xsl:mode> element:
<xsl:mode on-no-match="shallow-copy"/>
Iteration may sometimes be easier to write than recursion. The following code returns a running total of the integers from 1 through 10:
<xsl:iterate select="1 to 10">
    <xsl:param name="total" as="xs:integer" select="0"/>
    <xsl:variable name="newTotal" as="xs:integer" select="$total + ."/>
    <xsl:value-of select="concat($total, ' + ', . , ' = ' , $newTotal, '
')"/>
    <xsl:next-iteration>
        <xsl:with-param name="total" select="$newTotal"/>
    </xsl:next-iteration>
</xsl:iterate>
                This outputs the results of each iteration. To output only the final total,
                    remove the <xsl:value-of> statement and use
                        <xsl:on-completion>:
<xsl:iterate select="1 to 10">
    <xsl:param name="total" as="xs:integer" select="0"/>
    <xsl:on-completion select="$total"/>
    <xsl:variable name="newTotal" as="xs:integer" select="$total + ."/>
    <xsl:next-iteration>
        <xsl:with-param name="total" select="$newTotal"/>
    </xsl:next-iteration>
</xsl:iterate>
                although for this contrived problem it would, of course, be simpler to write
                        <xsl:value-of select="sum(1 to 10)"/>.
A recursive template call might look like:
<xsl:template match="/">
    <xsl:variable name="result">
        <xsl:call-template name="accumulate">
            <xsl:with-param name="total" select="0"/>
            <xsl:with-param name="range" select="1 to 10"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:sequence select="$result"/>
</xsl:template>
<xsl:template name="accumulate">
    <xsl:param name="total" as="xs:integer"/>
    <xsl:param name="range" as="xs:integer*"/>
    <xsl:choose>
        <xsl:when test="empty($range)">
            <xsl:sequence select="'done'"/>
        </xsl:when>
        <xsl:otherwise>
            <xsl:variable name="currentValue" as="xs:integer" select="$range[1]"/>
            <xsl:variable name="newTotal" as="xs:integer" select="$total + $currentValue"/>
            <xsl:value-of select=" concat($total, ' + ', $currentValue, ' = ', $newTotal, '
')"/>
            <xsl:call-template name="accumulate">
                <xsl:with-param name="total" as="xs:integer" select="$newTotal"/>
                <xsl:with-param name="range" as="xs:integer*" select="remove($range, 1)"/>
            </xsl:call-template>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
                This returns a report on each step plus the word done
 at the end. To see
                    just the steps, make <xsl:when> an empty element. To return
                    just the total, remove the <xsl:value-of> from the
                        <xsl:otherwise> element and set the value of the sequence
                    returned inside <xsl:when> to $total.
The following recursive function (not using <xsl:iterate>)
                    adds an additional consecutive value to a sequence of integers and counts the
                    number of values until it overflows the stack (at 1896, although your mileage
                    may vary):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math" exclude-result-prefixes="#all"
    xmlns:djb="http://www.obdurodon.org" version="3.0">
    <xsl:function name="djb:stuff" as="xs:integer*">
        <xsl:param name="in" as="xs:integer*"/>
        <xsl:message select="count($in)"/>
        <xsl:variable name="new" as="xs:integer*" select="$in, $in[last()] + 1"/>
        <xsl:sequence select="djb:stuff($new)"/>
    </xsl:function>
    <xsl:template name="xsl:initial-template">
        <xsl:sequence select="djb:stuff(1)"/>
    </xsl:template>
</xsl:stylesheet>
                The following example uses <xsl:iterate> to avoid the
                    overflow. It has been instructed arbitrarily to stop adding values after 1000000
                    because otherwise it would never stop:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math" exclude-result-prefixes="#all"
    version="3.0">
    <xsl:template name="xsl:initial-template">
        <xsl:iterate select="1 to 1000000">
            <xsl:param name="in" as="xs:integer*" select="1"/>
            <xsl:variable name="new" as="xs:integer*" select="$in, $in[last()] + 1"/>
            <xsl:message select="count($in)"/>
            <xsl:next-iteration>
                <xsl:with-param name="in" as="xs:integer*" select="$new"/>
            </xsl:next-iteration>
        </xsl:iterate>
    </xsl:template>
</xsl:stylesheet>