Standard XSLT 3.0 allows maps (as described in the previous section) to be returned from templates, stored in variables,
and so on, so this style of processing is perfectly possible without departing from the standard. However, the facilities for
declaring the type of these maps are very weak. The closest we can get is map(xs:string, item()*)
which is
satisfied by any map whose keys are strings.
Much of the debugging process for this stylesheet involves understanding the contents of these returned maps, and it is therefore frustrating that the XSLT 3.0 type system is so poor at validating these maps and reporting type errors. Saxon therefore introduces an extension to XSLT 3.0, called tuple types. Here is a declaration of the returned structure as a tuple type:
tuple( valid: xs:boolean?, (: indicates whether the subtree is valid (default = true) :) errors: element(vr:error)*, (: a list of errors found when validating the subtree :) value: xs:anyAtomicType*, (: the typed value of an element or attribute :) type: xs:string*, (: the types of the typed values, as component IDs :) lexical: xs:string?, (: the lexical form of a value after whitespace normalization :) id: xs:string*, (: any ID values found while validating an element or attribute :) id-map: map(xs:string, element())*, (: a mapping from ID values to elements :) idrefs: xs:string*) (: any IDREF values found while validating an element or attribute :)
This clearly documents the expected contents of the map much more precisely than the bland declaration
map(xs:string, item()*)
.
Tuples in Saxon are not a separate data type in the way that maps and arrays are separate data types. Rather, a tuple type
is an alternative way of constraining the content of a map. It defines the (string-valued) keys that can appear in the map,
and for each permitted key, the permitted type of the corresponding values. Declaring the expected type of a map in this
form gives much improved static and dynamic type checking. For example, attempting to reference a non-existing field using
the lookup expression $result?Value
can generate a static error message, as can its use in an inappropriate
context such as $result?valid eq "true"
.
Because tuple type declarations are often quite lengthy, as in this example, Saxon allows them to be declared once using a type alias:
<saxon:type-alias name="validation-outcome" type=" tuple( valid: xs:boolean?, (: indicates whether the subtree is valid (default = true) :) errors: element(vr:error)*, (: a list of errors found when validating the subtree :) value: xs:anyAtomicType*, (: the typed value of an element or attribute :) type: xs:string*, (: the types of the typed values, as component IDs :) lexical: xs:string?, (: the lexical form of a value after whitespace normalization :) id: xs:string*, (: any ID values found while validating an element or attribute :) id-map: map(xs:string, element())*, (: a mapping from ID values to elements :) idrefs: xs:string*) (: any IDREF values found while validating an element or attribute :) "/>
And the type can then be referenced wherever an as
attribute can appear, for example:
<xsl:template match="*" as="map(xs:string, item()*)" saxon:as="~validation-outcome"/>
The syntax of saxon:as
is an XPath SequenceType
augmented with Saxon-specific
syntax, in this case a reference to a type alias marked as such by the presence of the leading tilde (~). The semantics
of saxon:as
are that it provides type information additional to that contained in the as
attribute. Because (under the XSLT extensibility rules) attributes in the Saxon namespace are ignored by
XSLT processors other than Saxon, this whole mechanism enables Saxon to do extra compile-time and run-time type
checking, without in any way sacrificing the interoperability of the stylesheet: it still functions correctly
under other standards-conforming XSLT 3.0 processors.