Patterns in XSLT 4.0

In XSLT 4.0, patterns can match any kind of item: atomic items and function items, as well as nodes. There are different kinds of pattern: predicate patterns, type patterns, and GNode patterns.

Type patterns

In XSLT 4.0, the syntax of match patterns is extended to make it easier to match maps and arrays. These extensions are available from Saxon 13 provided that XSLT 4.0 is enabled.

In particular the syntax ~item-type predicate* is allowed in a pattern, where item-type is any item type, for instance:

A type pattern matches an item if the item is an instance of the given item type, and if it satisfies each of the predicates. Note that there is no coercion applied, so, for example, an untyped atomic value does not match the pattern ~xs:string.

If a named item type has been declared like this:

<xsl:item-type name="cx:complex" type="record(r as xs:double, i as 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>

Patterns for record types are particularly useful when JSON is processed using XSLT template rules. For example, a JSON object such as the following:

{ "firstName": "John", "lastName": "Doe", "email": "john.doe@example.com", "age": 45, "weight": 67 }

can be matched with a pattern such as match="~record(firstName, lastName, email, age, weight)". The order of properties is immaterial.

Predicates can be added as with any other pattern, for example match="~record(firstName, lastName, department)[?department='sales']".

For information on the rules for calculating the default priority of these patterns, see the specification.

Matching Nodes

In match patterns, union node tests work like union patterns, so match="@(code, ns:*)" is equivalent to having two separate template rules, matching the two node tests, with different default priority.

In Saxon 13, patterns using the traditional path syntax will only match XNodes, not JNodes. (This differs from the rules in the current 4.0 specification at the time of writing.)

A path pattern in Saxon 13 will match a JNode only if an explicit JNodeType is used. This uses the syntax "jnode" "(" Selector "," SequenceType ")". Here Selector matches the key property of the JNode, and may be given either as "*" (which matches any key), or as a Constant (which may be a string literal, a numeric literal with optional minus sign, a QName literal such as #xsi:type, or the constants true() and false()). So for example the pattern jnode("name", xs:string) matches a JNode whose key property is the string "name", and whose value property is an instance of xs:string. The quotes may be omitted from the key value if it takes the form of an NCName, so this could also be written jnode(name, xs:string).

Like other node tests, a JNode test can be qualified with an ancestor path (for example jnode(products, array(*))//jnode(price, xs:decimal)) and/or with predicates (for example jnode(price, xs:decimal)[empty(../discount)]).