Chapter 14. Assertions

image

Assertions are a powerful new feature in 1.1 that allows you to specify additional constraints using XPath 2.0. This addresses a significant limitation in XML Schema 1.0 that prevented the definition of co-constraints, where one data item affects the validity of another. It also generally allows for much more complex validation criteria to be expressed.

This chapter covers assertions, as well as conditional type assignment which allows the type of an element to be determined by an XPath expression on its attributes. Conditional type assignment is also new in version 1.1. Although this feature is separate from assertions, it has similar syntax and some overlapping use cases.

14.1. Assertions

Assertions are defined on types, rather than element or attribute declarations, so they are shared across all elements or attributes that have a particular type. Example 14–1 shows two types, one simple and one complex, that have assertions. For SizeType, it is testing to make sure that the value is not equal to zero. For ProductType, it is testing the validity of the product number, based on the department.

Example 14–1. Assertions on simple and complex types


<xs:simpleType name="SizeType">
  <xs:restriction base="xs:integer">
    <xs:assertion test="$value != 0"/>
  </xs:restriction>
</xs:simpleType>
<xs:complexType name="ProductType">
  <xs:sequence>
    <xs:element name="number" type="xs:integer"/>
    <xs:element name="name" type="xs:string"/>
    <xs:element name="size" type="SizeType"/>
  </xs:sequence>
  <xs:attribute name="dept" type="xs:string"/>
  <xs:assert test="(@dept = 'ACC' and number > 500) or
                   (number &lt; 300)"/>
</xs:complexType>


As you can see, two different elements are used: assertion is used in simple types (and in simple content), and assert is used in complex types. Both assertion and assert have a test attribute that specifies an XPath expression. The XPath returns a Boolean (true/false) value. If the expression is true, the element or attribute is valid with respect to the assertion. If it is false, it is invalid.

Assertions are specified using XPath 2.0, which is a powerful language that includes over a hundred built-in functions and many operators. This chapter describes some of the XPath 2.0 functions, operators, and expression syntax that are most useful for assertions, but it is by no means complete. For a complete explanation of all XPath operators and syntax, you can refer to the XML Path Language (XPath) 2.0 recommendation at www.w3.org/TR/xpath20.

Syntactically, any XPath 2.0 is allowed in an assertion. However, one limitation of assertions is that your XPath expression has to stay within the scope of the type itself. It can only access attributes, content, and descendants of the element that has that type. It cannot access the parent or other ancestor elements, siblings, separate XML documents, or any other nondescendant elements. This means that for cross-element validation, the assertion needs to be specified on an ancestor type that contains all of the elements or attributes mentioned in the assertion.

14.1.1. Assertions for simple types

Assertions in simple types are facets, and as such they appear alongside all the other facets inside a restriction element. That facet is called assertion, and its syntax is shown in Table 14–1.

Table 14–1. XSD Syntax: simple type assertion

Image

Example 14–2 shows an assertion on a simple type. Simple type assertions are generally less complicated than those for complex types because there are no descendants, only a value to test. A special built-in variable is used to access that value, called $value. There is no context item for a simple type assertion, so you cannot use a period (.) to represent the current element or value1 like you might in some XPath expressions.

Example 14–2. An assertion on a simple type


<xs:simpleType name="SizeType">
  <xs:restriction base="xs:integer">
    <xs:assertion test="$value != 0"/>
  </xs:restriction>
</xs:simpleType>


The assertion facet can also be used inside the restriction element for complex types with simple content, just like any other facet. Example 14–3 shows two complex types with simple content, one restricting the other by adding an assertion. However, if you need to access the attributes of that type in the assertion, you should use an assert instead, as shown later in Example 14–17.

Example 14–3. An assertion on the simple content of a complex type


<xs:complexType name="SizeType">
  <xs:simpleContent>
    <xs:extension base="xs:integer">
      <xs:attribute name="system" type="xs:string"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>
<xs:complexType name="RestrictedSizeType">
  <xs:simpleContent>
    <xs:restriction base="SizeType">
      <xs:assertion test="$value != 0"/>
    </xs:restriction>
  </xs:simpleContent>
</xs:complexType>


You can specify multiple assertions on the same simple type, in which case they must all return true for the element or attribute to be valid. Values of type DepartmentCodeType in Example 14–4 must be valid with respect to both specified assertions. Assertions can be combined with other facets, in any order. In fact, it is recommended that you continue to use other facets if they can express the constraint. For example, use the length facet as shown rather than an assertion with a test of string-length($value) = 3.

Example 14–4. A simple type with more than one assertion


<xs:simpleType name="DepartmentCodeType">
  <xs:restriction base="xs:token">
    <xs:assertion test="not(contains($value,'X'))"/>
    <xs:assertion test="substring($value,2,2) != '00'"/>
    <xs:length value="3"/>
  </xs:restriction>
</xs:simpleType>


14.1.1.1. Using XPath 2.0 operators

The XPath 2.0 language allows a number of operators in its syntax, for example for performing comparisons and arithmetic operations. Table 14–2 shows some of the operators that are likely to be used in simple type assertions, along with examples.

Table 14–2. Common XPath 2.0 operators

Image

Parentheses can be used in XPath to change the evaluation order of these operators. For example, by default, and takes precedence over or. The assertion in Example 14–5 uses parentheses around the first two comparisons to change the evaluation order. Without the parentheses, the second and third comparisons would have been combined by and before evaluating the or.

Parentheses can also be used to create a sequence of multiple values to test, as shown in the assertion in Example 14–6. The expression evaluates to true if the value is any of the three strings listed.

Example 14–5. Using parentheses to change evaluation order


<xs:simpleType name="SizeType">
  <xs:restriction base="xs:integer">
    <xs:assertion test="($value &lt; 12 or $value > 50)
                        and $value != 0"/>
  </xs:restriction>
</xs:simpleType>


Example 14–6. Using parentheses to create sequences


<xs:simpleType name="DepartmentCodeType">
  <xs:restriction base="xs:token">
    <xs:assertion test="$value = ('ACC','WMN','MEN')"/>
  </xs:restriction>
</xs:simpleType>


14.1.1.2. Using XPath 2.0 functions

XPath 2.0 includes over 100 built-in functions. Functions in XPath are called using a syntax that is probably familiar from other programming languages: the function name, followed by parentheses that contain the arguments to the function separated by commas. Table 14–3 provides a sample of built-in functions that would commonly be used in simple type assertions. For a complete list, refer to the XQuery 1.0 and XPath 2.0 Functions and Operators recommendation at www.w3.org/TR/xpath-functions.

Table 14–3. Common XPath 2.0 functions on single values

Image
Image

These functions are all built in, and you do not need to use name-space prefixes on their names. Your schema processor may support additional implementation-defined functions that are in other namespaces. Typically, in simple type assertions, you will be passing $value as one of the arguments. Table 14–4 shows some example values for simple type assertions that use common XPath functions.

Table 14–4. Examples of XPath 2.0 function calls

Image

Note that the matches function interprets regular expressions slightly differently from the pattern facet. The value of a pattern facet is the regular expression for the whole string, with implied anchors at the beginning and the end. The matches function, on the other hand, tests whether a string contains any substring that matches the pattern. To indicate that a pattern should match the start and/or end of the entire string, anchors ^ (for the start of a string) and $ (for the end of the string) must be used.

The examples in the table focus on assertions that cannot be expressed with other facets. For example, to simply test whether a value starts with ABC, you could use a pattern, as in <xs:pattern value="ABC.*"/>. However, it usually requires an assertion to express that a value must not match a pattern or an enumeration, or to indicate that processing should be case-sensitive.

14.1.1.3. Types and assertions

XPath 2.0 is a type-aware language, meaning that the processor pays attention to the types of values when performing operations on them. It is not valid in XPath 2.0 to compare an integer to a string, at least not with converting one value to the other’s type. Likewise, the built-in functions require arguments to be of a specific type. For example, the substring function will not accept an integer as the first argument, because it is expecting a string.

The processor is getting the information about the type of the value from the simple type definition itself. For example, if the simple type is a restriction of integer, then the value will be treated like an integer. Example 14–7 shows three simple types that have type errors in their assertions.

1. SizeType is in error because the value is an integer and it is being passed to the string-length function which expects a string.

2. DepartmentCodeType is in error because the value is a string but it is being compared to a number.

3. EffectiveDateTimeType is in error because the value is a date/time but it is being compared to a string.

Example 14–7. Assertions with type errors


<xs:simpleType name="SizeType">
  <xs:restriction base="xs:integer">
    <xs:assertion test="string-length($value) &lt; 2"/>
  </xs:restriction>
</xs:simpleType>
<xs:simpleType name="DepartmentCodeType">
  <xs:restriction base="xs:string">
    <xs:assertion test="$value != 001"/>
  </xs:restriction>
</xs:simpleType>
<xs:simpleType name="EffectiveDateTimeType">
  <xs:restriction base="xs:dateTime">
    <xs:assertion test="$value > '2000-01-01T12:00:00'"/>
  </xs:restriction>
</xs:simpleType>


Some processors will treat these type errors like dynamic errors, meaning that they are not reported as errors in the schema. Instead, dynamic errors simply cause the assertion to return false, rendering the element or attribute in question invalid. Most processors will issue warnings in these cases, though. XPath syntax errors and other static errors, on the other hand, will be flagged as errors in the schema by your processor.

To correct type errors like these, one should consider whether the simple types are being derived from the correct primitive types to start with. If you are performing arithmetic operations on a value, perhaps it should have a numeric type rather than a string type. For these examples, let’s assume that the primitive types were chosen correctly.

SizeType is really trying to limit the size of the integer. In this case, it makes sense to change it to use one of the bounds facets to limit the value of the integer, instead of trying to constrain its string representation.

For DepartmentCodeType, both operands in the comparison need to have the same type (or have types derived from each other). You could convert the $value to a numeric type, but the best approach here is to put quotes around the 001 to make it a string. Comparing them as strings takes into account the leading zeroes, which may be significant in a string-based department code.

For EffectiveDateType, as with the previous example, the operands need to be of comparable types. We could convert $value to a string, but then it would compare the values as strings instead of date/time values, which would mean that time zones may not be taken into account correctly. Instead, it is preferable to convert the second operand to a date/time type. This is done in XPath 2.0 using a type constructor, which is a special kind of function whose name is the appropriate built-in type name. It accepts a single argument, the value to be converted. For example, xs:dateTime('2000-01-01T12:00:00') converts the string to a date/time.

Example 14–8 shows our three examples, corrected to reflect the types of the values.

In addition to the type constructor functions, there is a string function that converts a value to a string, and a number function that converts a value to a floating-point number (double). Both of these functions also take a single argument, the value to be converted.

Example 14–8. Assertions with corrected type errors


<xs:simpleType name="SizeType">
  <xs:restriction base="xs:integer">
    <xs:maxExclusive value="100"/>
  </xs:restriction>
</xs:simpleType>
<xs:simpleType name="DepartmentCodeType">
  <xs:restriction base="xs:string">
    <xs:assertion test="$value != '001'"/>
  </xs:restriction>
</xs:simpleType>
<xs:simpleType name="EffectiveDateTimeType">
  <xs:restriction base="xs:dateTime">
    <xs:assertion test="$value >
                        xs:dateTime('2000-01-01T12:00:00')"/>
  </xs:restriction>
</xs:simpleType>


14.1.1.4. Inheriting simple type assertions

Like other facets, assertions are inherited when a simple type restricts another simple type. Any assertions that are specified in the restriction are added to the constraints on that value. In other words, a value must conform to the assertions on its simple type and on any other simple types it restricts, directly or indirectly. Values of type NonOverheadDepartmentCodeType in Example 14–9 must conform both to the assertion in that type and to the one specified in DepartmentCodeType.

Example 14–9. A simple type with inherited assertions


<xs:simpleType name="DepartmentCodeType">
  <xs:restriction base="xs:token">
    <xs:assertion test="not(contains($value,'X'))"/>
    <xs:length value="3"/>
  </xs:restriction>
</xs:simpleType>
<xs:simpleType name="NonOverheadDepartmentCodeType">
  <xs:restriction base="DepartmentCodeType">
    <xs:assertion test="substring($value,2,2) != '00'"/>
  </xs:restriction>
</xs:simpleType>


14.1.1.5. Assertions on list types

In most cases, $value evaluates to a single atomic value. However, when the value has a list type and consists of multiple items, $value is a sequence of multiple atomic values. You can still do a comparison, such as $value > 2, but that will return true if at least one of the values in the list is greater than 2. You can refer to specific values in the list using numeric predicates—for example, $value[1] to get the first value in the list. Example 14–10 shows an assertion on a list type stating that the first item in the list must be 0.

When working with multiitem sequences, there are a number of additional XPath functions that are useful. They are listed in Table 14–5. As we will see later, these functions are also useful on complex type assertions when there are repeating children—another example of multiitem sequences.

Table 14–5. Common XPath 2.0 functions on multiitem sequences

Image

Example 14–10. An assertion on a list type


<xs:simpleType name="SizeListType">
  <xs:restriction>
    <xs:simpleType>
      <xs:list itemType="xs:integer"/>
    </xs:simpleType>
    <xs:assertion test="$value[1] = 0"/>
  </xs:restriction>
</xs:simpleType>


Table 14–6 shows some additional examples of XPath tests that are appropriate for list types.

Table 14–6. Examples of assertion tests on list types

Image

The assertions in Table 14–6 apply to the list as a whole. If you want to constrain every value in the list, it makes more sense to put the assertion on the item type instead. Example 14–11 is a simple type SizeType that has one assertion on the item type of the list (testing that the value is less than 12) and one assertion on the list itself (testing the number of items in the list).

Example 14–11. Assertions on a list type and its item type


<xs:simpleType name="SizeListType">
  <xs:restriction>
    <xs:simpleType>
      <xs:list>
        <xs:simpleType>
          <xs:restriction base="xs:integer">
            <xs:assertion test="$value &lt; 12"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:list>
    </xs:simpleType>
    <xs:assertion test="count($value) > 2"/>
  </xs:restriction>
</xs:simpleType>


14.1.2. Assertions for complex types

For assertions on complex types, the assert element is used instead of assertion. The assert element, whose syntax is shown in Table 14–7, can appear in a complex type extension or restriction, or can appear directly as a child of complexType if neither simpleContent nor complexContent is used.

Table 14–7. XSD Syntax: complex type assertion

Image

Example 14–12 shows a constraint where the valid values of a child element (number) depend on the value of an attribute (dept). Constraints that cross multiple elements or attributes are sometimes called co-constraints and are a common use case for complex type assertions. If it were just a constraint on the number child individually, for example that it must be greater than 500 or less than 300, the assertion could have been put on the simple type of the number element. However, an assertion on the number element’s simple type would not have access to the dept attribute since it is out of scope, so the assertion must be moved up to the product parent.

Example 14–12. An assertion on a complex type


<xs:element name="product" type="ProductType"/>
<xs:complexType name="ProductType">
  <xs:sequence>
    <xs:element name="number" type="xs:integer"/>
    <xs:element name="name" type="xs:string"/>
    <xs:element name="size" type="xs:integer"/>
  </xs:sequence>
  <xs:attribute name="dept" type="xs:string"/>
  <xs:assert test="(@dept = 'ACC' and number > 500) or
                   (number &lt; 300)"/>
</xs:complexType>


The same XPath operators and functions described in Sections 14.1.1.1 on p. 355 and 14.1.1.2 on p. 357 can be used in these complex type assertions. One difference is that in complex content assertions, you do not use the $value variable (since there is no simple value) but instead use element and attribute names to indicate the values to be tested. Attribute names are preceded by an at sign (@) in XPath, as shown in the reference to @dept.

14.1.2.1. Path expressions

When element or attribute names are used, they are known as path expressions, and they are evaluated relative to the element being validated. In Example 14–12, it is looking for a dept that is an attribute of product (or any other element of type ProductType), and a number element that is a direct child of product.

Path expressions can also have multiple steps, separated by forward slashes, that access elements and attributes further down in the element content. Example 14–13 shows a complex type CatalogType that is one level up from ProductType. To access the number child of product from there, it uses the multistep path product/number. Relative to catalog, product/number brings back multiple number elements and passes them all as a sequence to the max function, then compares that maximum to the maxNumber attribute of catalog.

As an alternative to specifying the exact path down to a descendant element, you can use the shortcut .// before an element name to indicate a descendant. For example, .//number, relative to catalog, will bring back all number elements that are descendants anywhere within the catalog, at any level.

Path expressions often involve predicates, which are Boolean expressions in square brackets that filter the elements and attributes returned by the expression. An element to which a predicate is applied is only returned if the Boolean expression returns true. For example, product[number > 500] will test for products whose number is greater than 500. Table 14–8 shows some examples of assertions using predicates that could apply to CatalogType from Example 14–13.

Table 14–8. Examples of assertion with predicates

Image

Example 14–13. An assertion with a multistep path


<xs:element name="catalog" type="CatalogType"/>
<xs:complexType name="CatalogType">
  <xs:sequence>
    <xs:element name="product" type="ProductType"
                maxOccurs="unbounded"/>
  </xs:sequence>
  <xs:attribute name="maxNumber" type="xs:integer"/>
  <xs:assert test="not(max(product/number) > @maxNumber)"/>
</xs:complexType>
<xs:complexType name="ProductType">
  <xs:sequence>
    <xs:element name="number" type="xs:integer"/>
    <xs:element name="name" type="xs:string"/>
    <xs:element name="size" type="xs:integer"/>
  </xs:sequence>
  <xs:attribute name="dept" type="xs:string"/>
</xs:complexType>


For the second example in Table 14–8, you might think that you can use product[number > 500] to test that product numbers are greater than 500. However, that will return true if there is at least one product number greater than 500; it does not ensure that all of the products have a number greater than 500. Using the not function, as shown in the table, works because it tests that there aren’t any that are less than 500.

You may have noticed that most of the examples in the table actually return product elements rather than a Boolean true/false value. The results of XPaths used in assertions are automatically converted to a Boolean value. A sequence of one or more elements or attributes is treated as a “true” value, and an empty sequence (no elements or attributes) is treated as a “false” value.

14.1.2.2. Conditional expressions

The XPath 2.0 language also includes an if-then-else construct, known as a conditional expression, that is very useful for co-constraints. It uses if, then, and else keywords and the else clause is always required. The other syntactic requirement is that the if expression has to be in parentheses. Conditional expressions can be nested so that one conditional expression is embedded inside the then or else clause of another conditional expression.

Example 14–14 shows such an assertion which tests for different values of the dept attribute to determine the valid range of the number child. Since the else clause is always required, it simply calls the false function in the last clause, which means that if the department was not one of the three specified departments, the product is not valid, regardless of the number child.

Example 14–14. An assertion using conditional expressions


<xs:complexType name="ProductType">
  <xs:sequence>
    <xs:element name="number" type="xs:integer"/>
    <xs:element name="name" type="xs:string"/>
    <xs:element name="size" type="xs:integer"/>
  </xs:sequence>
  <xs:attribute name="dept" type="xs:string"/>
  <xs:assert test="if (@dept = 'ACC')
                   then number > 500
                   else if (@dept = 'WMN')
                   then number &lt;= 300 and number > 200
                   else if (@dept = 'MEN')
                   then number &lt; 200
                   else false()"/>
</xs:complexType>


14.1.2.3. Assertions in derived complex types

Assertions are inherited by derived complex types. Any assertions that are specified in an extension or restriction are added to the constraints on that type. In other words, an element must conform to the assertions on its complex type and on any other complex types from which its type is derived. Elements of type ExtendedProductType in Example 14–15 must conform both to the assertion in that type and the one specified in ProductType.

Example 14–15. Assertions in complex type extension


<xs:complexType name="ProductType">
  <xs:sequence>
    <xs:element name="number" type="xs:integer"/>
    <xs:element name="name" type="xs:string"/>
  </xs:sequence>
  <xs:attribute name="dept" type="xs:string"/>
  <xs:assert test="(@dept = 'ACC' and number > 500) or
                   (number &lt; 300)"/>
</xs:complexType>
<xs:complexType name="ExtendedProductType">
  <xs:complexContent>
    <xs:extension base="ProductType">
      <xs:sequence>
        <xs:element name="size" type="xs:integer" minOccurs="0"/>
      </xs:sequence>
      <xs:assert test="if (@dept = 'ACC')
                       then not(size)
                       else true()"/>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>


Assertions are also inherited in restrictions. Elements of type RestrictedProductType in Example 14–16 must conform both to the assertion in that type and the one specified in ProductType. Unlike the content model, which needs to be respecified in the restricted type definition, assertions are inherited automatically.

Example 14–16. Assertions in complex type restriction


<xs:complexType name="ProductType">
  <xs:sequence>
    <xs:element name="number" type="xs:integer"/>
    <xs:element name="name" type="xs:string" minOccurs="0"/>
    <xs:element name="size" type="xs:integer" minOccurs="0"/>
  </xs:sequence>
  <xs:attribute name="dept" type="xs:string"/>
  <xs:assert test="(@dept = 'ACC' and number > 500) or
                   (number &lt; 300)"/>
</xs:complexType>
<xs:complexType name="RestrictedProductType">
  <xs:complexContent>
    <xs:restriction base="ProductType">
      <xs:sequence>
        <xs:element name="number" type="xs:integer"/>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="size" type="xs:integer" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="dept" type="xs:string"
                    use="required"/>
      <xs:assert test="if (@dept = 'ACC')
                       then not(size)
                       else true()"/>
    </xs:restriction>
  </xs:complexContent>
</xs:complexType>


When a complex type with simple content extends a simple type, it can use assert to add assertions to the simple type. This is useful as an alternative to using assertion to restrict the content type, because the assert allows access to the attributes while the assertion doesn’t. The $value variable can be used in this case; just like with simple types, $value will contain the content of the element, with an appropriate data type. Example 14–17 shows an assertion that tests both the system attribute and the value of the element.

Example 14–17. An assertion on a complex type with simple content


<xs:complexType name="SizeType">
  <xs:simpleContent>
    <xs:extension base="xs:integer">
      <xs:attribute name="system" type="xs:string"/>
      <xs:assert test="if (@system='US')
                       then $value &lt; 20
                       else $value >= 20"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>


14.1.3. Assertions and namespaces

If your schema has a target namespace, it is necessary to correctly prefix the element and attribute names used in XPath expressions. Example 14–18 shows a schema with a target namespace as well as a name-space declaration that maps that namespace to the prefix prod. The element names used in the assertion XPaths are then prefixed with prod to indicate that they are in that namespace. Otherwise, the processor would be looking for those elements in no namespace.

Example 14–18. Assertions using prefixed element names


<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://datypic.com/prod"
           xmlns:prod="http://datypic.com/prod"
           elementFormDefault="qualified">
  <xs:element name="product" type="ProductType"/>
  <xs:complexType name="ProductType">
    <xs:sequence>
      <xs:element name="number" type="xs:integer"/>
      <xs:element name="name" type="xs:string"/>
      <xs:element name="size" type="xs:string" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="dept" type="xs:string"/>
    <xs:assert test="(@dept = 'ACC' and prod:number > 500) or
                     (prod:number &lt; 300)"/>
    <xs:assert test="if (@dept = 'ACC')
                     then not(prod:size)
                     else true()"/>
  </xs:complexType>
</xs:schema>


Note the fact that elementFormDefault is set to qualified, which is what puts the locally declared number and size in the target namespace. Otherwise, you wouldn’t need to prefix their names in the XPath, since they would be unqualified. For more information on qualified and unqualified element names, see Section 6.3 on p. 98.

14.1.3.1. Using xpathDefaultNamespace

You might expect to be able to declare a default namespace, such as xmlns="http://datypic.com/prod", to avoid having to prefix the element names. However, regular default namespace declarations do not apply to XPath expressions. You can, however, use an xpathDefaultNamespace attribute to designate the default namespace for all unprefixed element names that are used in the XPath. As with regular default namespace declarations, xpathDefaultNamespace does not affect attribute names.

Example 14–19 uses the xpathDefaultNamespace attribute on the schema element. This means that the element names number and size in the XPaths are interpreted as being in the http://datypic.com/prod namespace. It is not looking for the dept attribute in that namespace. This is appropriate since the attributeFormDefault is defaulting to unqualified, meaning that locally declared attributes are in no namespace.

Example 14–19. Assertions using xpathDefaultNamespace


<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://datypic.com/prod"
           xmlns:prod="http://datypic.com/prod"
           elementFormDefault="qualified"
           xpathDefaultNamespace="http://datypic.com/prod">
  <xs:element name="product" type="prod:ProductType"/>
  <xs:complexType name="ProductType">
    <xs:sequence>
      <xs:element name="number" type="xs:integer"/>
      <xs:element name="name" type="xs:string"/>
      <xs:element name="size" type="xs:string" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="dept" type="xs:string"/>
    <xs:assert test="(@dept = 'ACC' and number > 500) or
                     (number &lt; 300)"/>
    <xs:assert test="if (@dept = 'ACC')
                     then not(size)
                     else true()"/>
  </xs:complexType>
</xs:schema>


Instead of containing a specific namespace name, the xpathDefaultNamespace attribute can contain one of three special keywords:

##targetNamespace, indicating that the default XPath namespace is the same as the target namespace

##defaultNamespace, indicating that the default XPath namespace is the namespace that is declared as the default (with an xmlns= attribute)

##local, indicating that there is no default XPath namespace

In Example 14–19, changing the value to ##targetNamespace would have the same meaning, since http://datypic.com/prod is the target namespace.

It is most convenient to specify xpathDefaultNamespace on the schema element, in which case it applies to all XPath expressions in the schema document. It can also be specified on (and is relevant to) the following elements:

• The assert and assertion elements, where it affects the test attribute

• The alternative element, where it affects the test attribute

• The selector and field elements, where it affects the xpath attribute

If xpathDefaultNamespace does not appear on one of these elements, the value is taken from the schema. If no value is provided for the schema, the default value is no namespace, meaning that unprefixed element names are interpreted as not being in a namespace.

14.2. Conditional type assignment

Another new feature in XML Schema 1.1 is conditional type assignment, which allows for the type of an element to be assigned based on the values and/or presence of its attributes. A set of type alternatives are specified using alternative elements, which appear as children of the element declaration.

14.2.1. The alternative element

The syntax of an alternative element is shown in Table 14–9. It has a test attribute that specifies the condition under which that type is selected, in the form of an XPath 2.0 expression. It also indicates the type if this condition is true, which is signified either by a type attribute or an anonymous simpleType or complexType child.

Table 14–9. XSD Syntax: type alternative

Image

14.2.2. Specifying conditional type assignment

Example 14–20 shows an example of conditional type assignment where there are three type alternatives:

1. The first alternative indicates that if the value of the dept attribute is ACC, the type assigned to the element declaration is AccessoryType.

2. The second alternative indicates that if the value of the dept attribute is either WMN or MEN, the type assigned is ClothingType.

3. The third alternative has no test attribute, indicating that ProductType is the default type if neither of the two other alternatives apply.

Example 14–20. Conditional type assignment with default type


<xs:element name="product">
  <xs:alternative test="@dept='ACC'" type="AccessoryType"/>
  <xs:alternative test="@dept='WMN' or @dept='MEN'"
                  type="ClothingType"/>
  <xs:alternative type="ProductType"/>
</xs:element>


The processor will run through the alternatives and choose the first one in order whose test returns true. If none of the tests return true, and there is a default type specified by an alternative with no test attribute, as there is in Example 14–20, that alternative indicates the type.

It is also possible to use type alternatives even though you have already declared a type in the usual way, giving element a type attribute or a simpleType or complexType child. An example is shown in Example 14–21, where the type attribute is used on element to assign the type ProductType to the element.

This is saying that ProductType is the type for product unless one of the alternatives applies. It is similar to the previous example, but defining it this way comes with the additional constraint that the type alternatives must be derived from the declared type. In this case, AccessoryType and ClothingType must be derived (directly or indirectly) from ProductType.

Example 14–21. Conditional type assignment with declared type


<xs:element name="product" type="ProductType">
  <xs:alternative test="@dept='ACC'" type="AccessoryType"/>
  <xs:alternative test="@dept='WMN' or @dept='MEN'"
                  type="ClothingType"/>
</xs:element>


A third possibility is that neither a declared type nor a default type is specified, as in Example 14–22. In that case, if no alternatives apply, a product element can contain any well-formed XML; its type is anyType.

Example 14–22. Conditional type assignment with no default


<xs:element name="product">
  <xs:alternative test="@dept='ACC'" type="AccessoryType"/>
  <xs:alternative test="@dept='WMN' or @dept='MEN'"
                  type="ClothingType"/>
</xs:element>


14.2.3. Using XPath in the test attribute

Only a very small subset of the XPath 2.0 syntax is allowed in the test attribute by default, although some implementations may choose to support a more complete subset. The only XPath functions and operators that are allowed are:

and and or Boolean operators

• Comparison operators (=, !=, <, <=, >, >=)

• The not function

• The type constructor functions

In addition, the XPath expression can only access the attributes of the element being validated. It cannot access its parent or ancestors, and it cannot even access its children or descendants like assertions can.

Additional example values for the test attribute are shown in Table 14–10.

Table 14–10. Examples of type alternative tests

Image

The last example in the table makes use of the integer type constructor function to ensure that the two values are being compared as numbers. Otherwise, they would be compared as strings, and a string 100 is considered to be less than a string 99.

This highlights an important difference between assertions and conditional type assignment with regard to types in XPath. In assertions, type information is used in the XPath expressions because there is only one type to consider. In the case of conditional type assignment, the type has not even been assigned yet, so it is impossible to determine the types of the attributes. When num is compared to a literal integer, as in the second-to-last example, it is automatically converted to an integer. But when num and maxNum are compared to each other, and neither has a type, they need to be converted to integers to ensure that they are compared appropriately.

14.2.4. The error type

A special built-in simple type named error (in the XML Schema namespace) is defined for use in conditional type assignment.1 It is used to indicate that a validation error should be raised under certain conditions.

Example 14–23 uses the error type to raise an error if the dept attribute is equal to anything other than ACC, WMN, or MEN.

Example 14–23. Using the error type as the default


<xs:element name="product">
  <xs:alternative test="@dept='ACC'" type="AccessoryType"/>
  <xs:alternative test="@dept='WMN' or @dept='MEN'"
                  type="ClothingType"/>
  <xs:alternative type="xs:error"/>
</xs:element>


It doesn’t have to just be the last alternative, with no test, that uses the error type. It can be used with a test, and as an earlier alternative, as shown in Example 14–24. This example will raise an error if the product does not have a dept attribute.

Example 14–24. Using the error type with a test


<xs:element name="product">
  <xs:alternative test="not(@dept)" type="xs:error"/>
  <xs:alternative test="@dept='ACC'" type="AccessoryType"/>
  <xs:alternative test="@dept='WMN' or @dept='MEN'"
                  type="ClothingType"/>
</xs:element>


14.2.5. Conditional type assignment and namespaces

As with assertions, if the schema has a target namespace, you may need to pay attention to namespace prefixes in your XPath. It is less likely to be an issue because you are only using attribute names and it is less common for attributes to be in the target namespace of the schema. However, if an attribute is in a namespace, for example because it is globally declared or because attributeFormDefault is set to qualified, its name does need to be prefixed.

Example 14–25 shows a revised example where dept is globally declared, which means that it is in the target namespace. The XPath must now reflect the target namespace, so a prod prefix is added to dept wherever it appears in the XPaths.

As with assertions, the xpathDefaultNamespace attribute affects the XPaths in type alternatives. However, since it does not affect attribute names, it is unlikely to be useful in conditional type assignment.

Example 14–25. Conditional type assignment with globally declared attribute


<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://datypic.com/prod"
           xmlns:prod="http://datypic.com/prod"
           elementFormDefault="qualified">
  <xs:element name="product" type="prod:ProductType">
    <xs:alternative test="@prod:dept='ACC'"
                    type="prod:AccessoryType"/>
    <xs:alternative test="@prod:dept='WMN' or @prod:dept='MEN'"
                    type="prod:ClothingType"/>
  </xs:element>
  <xs:complexType name="ProductType">
    <xs:sequence>
      <xs:element name="number" type="xs:integer"/>
      <xs:element name="name" type="xs:string"/>
    </xs:sequence>
    <xs:attribute ref="prod:dept"/>
  </xs:complexType>
  <xs:attribute name="dept" type="xs:string"/>
  <!--...-->
</xs:schema>


14.2.6. Using inherited attributes in conditional type assignment

Section 7.6 on p. 126 introduced the concept of inherited attributes, which are relevant not just to an element but also to its descendant elements. Example 14–26 shows an instance example where a language attribute is intended to be inherited by the first title element from its parent workTitles.

Example 14–26. Instance with an inherited attribute


<workTitles language="en">
  <title>Time Transfixed</title>
  <title language="fr">La Durée poignardée</title>
</workTitles>


Example 14–27 is a schema that defines a type alternative for the title element. It says that if the language is English, it has the type EnglishTitleType, meaning that the contents can only contain basic Latin characters (which is admittedly very simplified). Otherwise, the title element has the less restrictive type TitleType that allows any string.

Example 14–27. Assertions on inherited attribute


<xs:element name="workTitles" type="WorkTitlesType"/>
<xs:complexType name="WorkTitlesType">
  <xs:sequence>
    <xs:element name="title" maxOccurs="unbounded"
                type="TitleType">
      <xs:alternative test="@language='en'"
                      type="EnglishTitleType"/>
    </xs:element>
  </xs:sequence>
  <xs:attribute name="language" type="xs:language"
                inheritable="true"/>
</xs:complexType>
<xs:complexType name="EnglishTitleType">
  <xs:simpleContent>
    <xs:restriction base="TitleType">
      <xs:pattern value="p{IsBasicLatin}+"/>
    </xs:restriction>
  </xs:simpleContent>
</xs:complexType>
<xs:complexType name="TitleType">
  <xs:simpleContent>
    <xs:extension base="xs:string">
      <xs:attribute name="language" type="xs:language"/>
    </xs:extension>
  </xs:simpleContent>
</xs:complexType>


The interesting thing about this example is that although the first title element does not have a language attribute in its start tag in the instance, in the XPath expressions it is treated as if it does, because the attribute is inherited. The instance in Example 14–26 is valid according to this schema.

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

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