Declaring Map Types

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.