Constructors and Casting

There are two mechanisms in XQuery for changing values from one type to another: constructors and casting.

Constructors

Constructors are functions used to construct atomic values with given types. For example, the constructor xs:date("2006-05-03") constructs an atomic value whose type is xs:date. The signature of this xs:date constructor function is:

xs:date($arg as xs:anyAtomicType?) as xs:date?

There is a constructor function for each of the built-in atomic types (both primitive and derived). The qualified name of the constructor is the same as the qualified name of the type. For the built-in types, constructor names are prefixed with xs to indicate that they are in the XML Schema namespace.

All of the constructor functions have a similar signature, in that they accept an atomic value and return an atomic value of the appropriate type. Because function arguments are atomized, you can pass a node to a constructor function, and its typed value is extracted. If you pass an empty sequence to a constructor, the result will be the empty sequence.

Unlike most other functions, constructor functions will accept arguments of any type and attempt to cast them to the appropriate type.[*] The argument value must have a type that can be cast to the new type; otherwise, a type error is raised. Values of almost all types can be cast to and from xs:string and xs:untypedAtomic. The specific rules for casting among types are described in "Casting Rules," later in this chapter.

In addition, the value must also be valid for the new type. For example, although the rules allow you to cast an xs:string value to xs:date, the expression xs:date("2006-13-02") raises an error because the month 13 is invalid.

Constructors also exist for all named user-defined atomic types that are in the in-scope schema definitions. If, in a schema, you have defined a type prod:SizeType that is derived from xs:integer by setting minInclusive to 0 and maxInclusive to 24, you can construct a value of this type using, for example:

prod:SizeType("10")

The qualified names must match, so the prefix prod must be mapped to the target namespace of the schema containing the SizeType definition. If the type name is in no namespace (the schema in which it is defined has no target namespace), you cannot use a constructor (unless you change the default function namespace, which is not recommended). You must use a cast expression instead.

The Cast Expression

Casting is the process of changing a value from one type to another. The cast expression can be used to cast a value to another type. It has the same meaning as the constructor expression; it is simply a different syntax. The only difference is that it can be used with a type name that is in no namespace. For example:

$myNum cast as xs:integer

casts the value of $myNum to the type xs:integer. It is equivalent to xs:integer($myNum). The syntax of a cast expression is shown in Figure 11-4.

Syntax of a cast expression

Figure 11-4. Syntax of a cast expression

The cast expression consists of the expression to be cast, known as the input expression, followed by the keywords cast as, followed by the qualified name of the target type. Only a named atomic type can be specified, either a built-in type or a user-defined simple type whose definition is among the in-scope schema definitions.

The name of the atomic type may optionally be followed by a question mark as an occurrence indicator. In this case, the cast expression evaluates to the empty sequence if the input expression evaluates to the empty sequence. If no question mark is used, the input expression cannot evaluate to the empty sequence, or a type error is raised. This is in contrast to constructors, which always allow the empty sequence.

You cannot use the other occurrence indicators + and * because you cannot cast a sequence of more than one item using a cast expression. To cast more than one value, you could place your cast expression as the last step of a path, as in:

doc("catalog.xml")//number/(. cast as xs:string)

The input expression can evaluate to a node (in which case it is atomized to retrieve its typed value) or an atomic value. As with constructors, the value must have a type that allows casting to the target type, and it must also be a valid value of the target type.

The Castable Expression

The castable expression is used to determine whether a value can be cast to another specified atomic type. It is sometimes useful to determine this before the cast takes place to avoid dynamic errors, or to determine how the expression should be processed. For example:

if ($myNum castable as xs:integer)
then $myNum cast as xs:integer
else ( )

evaluates to $myNum cast to xs:integer if that is valid, otherwise the empty sequence. If the castable expression had not been used to test this, and $myNum was not castable as an xs:integer, an error would have been raised. The syntax of a castable expression is shown in Figure 11-5.

Syntax of a castable expression

Figure 11-5. Syntax of a castable expression

The castable expression consists of an expression, followed by the keywords castable as, followed by the qualified name of the target type. It evaluates to a Boolean value. As with the cast expression, you can use the question mark as an occurrence indicator. The castable expression determines not only whether the one type can be cast to the other type, but also whether that specific value is valid for that type.

Casting Rules

This section describes the rules for casting atomic values between specific types. These rules are used in cast expressions and constructors. In this section, the source type refers to the type of the original value that is being cast, and the target type refers to the type to which the value is being cast.

Casting among the primitive types

Specific rules exist for casting between each combination of two primitive types. These rules are discussed, along with the types themselves, in Appendix B. The rules can be summarized as follows:

  • Values of any atomic type can be cast to and from xs:string and xs:untypedAtomic if the value is valid for the target type. See the next two sections for more information.

  • A value of a numeric type can be cast to any other numeric type if the value is in the value space of the target type.

  • A value of a date, time, or duration type can sometimes be cast to another date, time, or duration type.

  • Other types (xs:boolean, xs:QName, xs:NOTATION, xs:anyURI, xs:hexBinary, and xs:base64Binary) have limited casting ability to and from types other than xs:string and xs:untypedAtomic. See Appendix B for more information on each type.

Casting to xs:string or xs:untypedAtomic

A value of type xs:string or xs:untypedAtomic can be cast to any other primitive type. For example, xs:integer("12") casts the xs:string value 12 to xs:integer. Of course, the string must represent a valid lexical representation of the target type. For example, xs:integer("12.1") raises an error because the lexical representation of xs:integer does not allow fractional parts.

When a value is cast from xs:string to another primitive type, whitespace is collapsed. Specifically, this means that every tab, carriage return, and line feed character is replaced by a single space; consecutive spaces are collapsed into one space; and leading and trailing spaces are removed. Therefore, xs:integer(" 12 ") is valid, even with the leading and trailing whitespace.

Casting to xs:string or xs:untypedAtomic

An atomic value of any type can be cast to xs:string or to xs:untypedAtomic. Some types have special rules about how their values are cast to xs:string. For example, integers have their leading zeros stripped. The rules (if any) for each type are described in Appendix B. Table 11-5 shows some examples of casting to xs:string and xs:untypedAtomic.

Table 11-5. Examples of casting to xs:string and xs:untypedAtomic

Expression

Value

xs:string("012")

"012"

xs:string(012)

"12"

xs:string(xs:float(12.3E2))

"1230"

xs:untypedAtomic(xs:float(12))

12 (of type xs:untypedAtomic)

xs:string(true( ))

"true"

Casting among derived types

Now that you have seen casting among the primitive types, let's look at derived types. There are three different cases.

The first case is that the source type is derived by restriction from the target type. In this case, the cast always succeeds because the source type is a subset of the target type. For example, an xs:byte value can always be cast to xs:integer.

The second case is that the source type and the target type are derived by restriction from the same primitive type. In this case, the cast succeeds as long as the value is in the value space of the target type. For example, xs:unsignedInt("60") can be cast to xs:byte, but xs:unsignedInt("6000") cannot, because 6000 is too large for xs:byte. This case also applies when the target type is derived by restriction from the source type. For example, xs:integer("25") can be cast to xs:unsignedInt, which is derived from it.

The third case is that the source type and the target type are derived by restriction from different primitive types—for example, if you want to cast a value of xs:unsignedInt to dty:myFloat, which is derived by restriction from xs:float. In this case, the casting process has three steps:

  1. The value is cast to the primitive type from which it is derived, e.g., from xs:unsignedInt to xs:decimal.

  2. The value is cast from that primitive type to the primitive type from which the target type is derived, e.g., from xs:decimal to xs:float.

  3. The value is cast from that primitive type to the target type, e.g., from xs:float to dty:myFloat.



[*] This is because constructor functions do not rely on function conversion rules to perform automatic casts; the signature accepts arguments of any type and it is part of the function's purpose to cast those values to the appropriate type.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.75.217