The typeswitch expression provides a convenient syntax for performing special processing based on the type of an expression. An example is shown in Example 14-1.
Example 14-1. Binding variables to typeswitch expressions
typeswitch ($myProduct) case element(*,prod:HatType) return xs:string($myProduct/size) case element(*,prod:ShirtType) return xs:string(concat($myProduct/size/@system, ": ", $myProduct/size)) case element(*,prod:UmbrellaType) return "none" default return "n/a"
The example assumes that $myProduct
is bound to a product
element. In the schema, assume that product
elements are declared to have type ProductType
. However, in the input document, a product
element may carry an xsi:type
attribute that indicates another type that is derived by extension from ProductType
, such as HatType
, ShirtType
, or UmbrellaType
.
ProductType
itself does not allow a size
child. Depending on which subtype it has, it may or may not have a size
child. The typeswitch expression will return a different value depending on the type annotation of the product element.
The syntax of a typeswitch expression is shown in Figure 14-1. The typeswitch
keyword is followed by an expression in parentheses (called the operand expression), which evaluates to the sequence whose type is to be evaluated. This is followed by one or more case
clauses, plus a required default
clause that indicates the value if none of the case
clauses applies.
The processor uses sequence type matching (described in Chapter 11) to determine whether a case
clause applies. This means that if the type of the items in the sequence is the same as, or is derived from, the type identified by the case
clause, it matches. If more than one case
clause is applicable, the first one is used. Remember, with sequence type matching, it is not enough that the items are valid according to the specified type, they must actually have been validated and have that type (or a type derived from it) as their type annotation.
Each of the case
and default
clauses can have an optional variable name before the return
keyword. That variable is bound to the value of the operand expression. This is useful if the return expression is dependent on the sequence being tested. In Example 14-2, the $h
, $s
, and $p
variables are bound to the $myProduct
value, and the return expression references the variable. This example uses user-defined schema types, which are explained in the previous chapter.
Example 14-2. Binding variables to typeswitch expressions
typeswitch ($myProduct) case $h as element(*,prod:HatType) return xs:string($h/size) case $s as element(*,prod:ShirtType) return xs:string(concat($s/size/@system, ": ", $s/size)) case element(*,prod:UmbrellaType) return "none" default return "n/a"
The typeswitch expression can serve as shorthand for multiple if-then-else expressions that use instance of
expressions to determine the type of the variable. Example 14-3 shows this alternative, which is similar to Example 14-2.
Example 14-3. Alternative to a typeswitch expression
if ($myProduct instance of element(*,prod:HatType)) then xs:string($myProduct/size) else if ($myProduct instance of element(*,prod:ShirtType)) then xs:string(concat($myProduct/size/@system, ": ", $myProduct/size)) else if ($myProduct instance of element(*,prod:UmbrellaType)) then "none" else "n/a"
However, there is an important difference between the two: an implementation that supports static typing will raise a type error with Example 14-3. This is because, as far as the processor knows, $myProduct
is of type ProductType
, which does not allow a size
child. Even though the processor is aware that there are subtypes that allow a size
child, the static typing feature does not extend to parsing out the if expressions to determine that you would never evaluate the expression $myProduct/size
on anything that didn't allow a size
child. The typeswitch expression in Example 14-2, on the other hand, assures the processor that the branch that contains $h/size
will only ever be evaluated for elements of type HatType
. Remember that static typing is pessimistic; it will give you an error if anything could go wrong, not only if it knows that things will go wrong.
3.141.197.251