Saxon extensions to the W3C XSLT/XQuery specifications

Extension Functions

For the extension functions saxon:highest and saxon:lowest, the function that computes the key may now return an empty sequence, in which case the relevant item from the input is excluded from consideration (it will not feature in the result). The implementation has been made more robust in edge cases, for example where the key returns a value of an unordered type, or where the set of keys are not mutually comparable, or where they include NaN values.

A new function saxon:map-search is introduced. It is similar to map:find, in that it searches recursively through nested maps and arrays, but is more versatile. It differs from map:find in two main respects:

  1. Whereas map:find only takes as input the key value to look for, saxon:map-search also takes a function that can be used to test the associated value.
  2. Whereas map:find returns the value associated with the specified key, saxon:map-search returns much more information about where the match was found.

The new function saxon:with-pedigree() takes a map or array as input (typically, the result of parsing JSON), and returns an equivalent map or array whose implementation has special behavior: any downward selections of nested maps/arrays using subscripting or key lookup return a map/array that has "pedigree" information; the pedigree information tells you how the map/array was selected within its containing tree, and enables you to retrace your steps. If a map or array has pedigree information, the saxon:pedigree() function returns a map, with an entry "container" identifying the containing map or array, and an entry "key" or "index" identifying the key value in the containing map, or the index value in the containing array.

The new (higher-order) extension function saxon:group-starting splits a sequence into groups, based on a starting condition for each group. For example, the function call saxon:group-starting(following-sibling::*, fn{boolean(self::h1)})[1]?* selects the following-sibling elements of the context item up to (and excluding) the first h1 element.

The new extension function saxon:object-map converts a Java object to an XDM map. For more information, see the "Extensibility mechanisms" changes category.

Extension Attributes

An extension attribute saxon:options has been added to xsl:evaluate: the value is an expression, which must evaluate to a map. The map allows additional Saxon-specific options for expression evaluation to be defined. Initially two options are defined:

An accumulator rule for a streamable accumulator that specifies phase="end" may now also specify saxon:capture="yes". This relaxes the rule that the accumulator evaluation must be motionless; instead it can now be consuming. When the "end-element" event for the accumulator rule is reached, the rule now has access to a snapshot of the entire element content (made using the fn:snapshot function). To avoid the snapshots become over-large, this is best used on elements that have minimal content; in such cases it can make programming an accumulator significantly easier.

Elements that have an as attribute to define a required type (for example, xsl:variable, xsl:param, and xsl:function) now additionally recognize a saxon:as attribute that can be used to provide additional type information using Saxon extension syntax. For example, if the as attribute specifies as="map(xs:string, xs:double)" for a map used to represent a complex number, then the additional attribute saxon:as="tuple(i: xs:double, r: xs:double)" may be used to indicate the required keys that must be present in the map. Similarly, if a function returns either a date or dateTime and indicates this using as="xs:anyAtomicType", the return type may be more precisely described using the syntax saxon:as="union(xs:date, xs:dateTime)". This allows Saxon extended types to be used without sacrificing stylesheet portability.

Extension Instructions

A new XSLT extension instruction saxon:do is provided: it is designed for use when invoking XPath expressions that have side-effects. For example, <saxon:do action="$connection?close()"/> calls the function $connection?close(), while <saxon:do action="saxon:discard-document($doc)"/> calls saxon:discard-document(). Any result of the expression is discarded. The instruction is useful in preference to xsl:sequence firstly because the result of the expression is discarded, and secondly because it is recognized by the optimizer as signaling an expression that is being evaluated for its side-effects. The optimizer will attempt to avoid aggressive optimizations such as lifting the instruction out of an xsl:for-each loop. Note however that this does not propagate; just because a function F calls saxon:do, for example, does not prevent calls on F being moved out of a loop.

The extension XSLT instruction saxon:tabulate-maps takes selected maps in a structure containing maps and arrays, such as might be derived by parsing JSON input, and returns these maps, expanded with information taken from containing maps and arrays.

The saxon:deep-update instruction creates a new map or array that is the same as an existing map or array except for modified content that may be several layers deep within nested maps and arrays. Typically (but not necessarily) the map or array to which it is applied will have been constructed by parsing JSON content using the parse-json() function.

As a result of the continuing popularity of the DocBook 1.0 stylesheets, which rely on the saxon:output extension element present in Saxon 6.5.5 and earlier releases, the extension instruction saxon6:output (in namespace xmlns:saxon6="") has been reinstated. It is a synonym for xsl:result-document. This enables the DocBook 1.0 stylesheets to run without error using Saxon-PE/EE 9.9.

XSLT Update Extension (Requires Saxon-EE). New extension instructions have been introduced to provide an XSLT equivalent of the XQuery Update capability. For example, you can delete selected attributes using the construct:

<saxon:update select="//chapter[1]"> <saxon:delete select=".//para[@deleted='yes']"/> </saxon:update>

The outermost instruction is saxon:update. This has a select attribute to select the nodes whose subtrees will be updated. The default is select=".". The result of the instruction is a sequence of nodes that are modified copies of the selected nodes (unless any of these nodes are deleted, in which case they are omitted from the result). The original tree remains unchanged.

Within the saxon:update instruction, the following subsidiary instructions may be evaluated: saxon:change, saxon:delete, saxon:insert, saxon:rename, saxon:replace.

The update extension for XSLT does not provide any equivalent to XQuery updating functions; all update instructions must be contained lexically within the saxon:update instruction.

XPath Syntax Extensions

Saxon 9.9 implements a number of extensions to the XPath 3.1 grammar, for more information see Syntax extensions. These are enabled only if the configuration option ALLOW_SYNTAX_EXTENSIONS is set, and they require Saxon-PE or higher.

Some of these syntax extensions were issued experimentally in Saxon 9.8, and the detailed rules have been refined in the light of experience.

The tuple type syntax, for example tuple(i: xs:double, r: xs:double), now allows a "?" after the field name to indicate that the field may be absent from the map, and allows a final ", *" to indicate that extra undeclared entries are permitted in the map.

andAlso and orElse operators. These operators (often called short-circuit operators) have the same effect as and and or, but they are guaranteed to evaluate the left-hand operand before the right-hand operand, and to avoid evaluating the right-hand operand when it is not needed. For example if ($x castable as xs:date andAlso xs:date($x) > current-date())... ensures that no failure occurs when $x is not a valid date. (The XPath specification offers no such guarantee for the and and or operators. Saxon will occasionally rearrange the order of evaluation, as permitted by the specification, if it thinks this will improve performance.)

XSLT syntax extensions

The syntax of match patterns is extended to make it easier to match maps and arrays (see XSLT syntax extensions: match patterns).

In particular the syntax ma-type predicate* is allowed, where ma-type is any of:

  1. ~type-alias
  2. tuple(...)
  3. map(...)
  4. array(...)

For example if a type alias has been declared:

<saxon:type-alias name="cx:complex" type="tuple(r: xs:double, i: xs:double)"/>

Then it can be used in a match pattern to match instances of the type, with or without predicates:

<xsl:template match="~cx:complex[?i=0]">{?r}</xsl:template><xsl:template match="~cx:complex">{?r}{if (?i ge 0) then '+' else ''}{?i}i</xsl:template>

The construct ~T at the start of a pattern can be regarded as an abbreviation for .[. instance of ~T].