Element Definitions

Custom Datatypes

The XML Schema type system is highly extensible. It provides a mechanism for defining new datatypes that inherit from either built-in datatypes or custom datatypes. Datatypes fall into two categories, simple types and complex types. Simple types cannot contain subelements or attributes; custom types can.

Simple Types

Simple types are datatypes that can be used to describe the type of data contained within an element or an attribute. Instances of simple types cannot contain attributes or other elements. Examples of simple types include int, long, string, and dateTime. A simple type can also define an enumeration or a union.

A simple type definition always derives from another simple type. The three types of derivations allowed by XML Schema are by restriction, by list, and by union.

A simple type derived from its base type by restriction can define additional restrictions imposed on the values that instances of the simple type can contain. Therefore, instances of a simple type derived by restriction can contain only a subset of the values that can be contained by its base type.

For example, the built-in double type is a restricted version of the decimal type. Instances of the decimal type are unbounded, and instances of the double type are restricted to values that meet the IEEE single-precision 64-bit floating type. Following are a couple of simple type definitions that derive by restriction.

<?xml version='1.0'?> <schema xmlns="http://www.w3.org/2001/XMLSchema.xsd">   <simpleType name="MyInt">     <restriction base="int"/>   </simpleType>   <simpleType name="GenericProductId">     <restriction base="string">       <minLength value="1"/>       <maxLength value="20"/>     </restriction>   </simpleType>   <simpleType name="Percent">     <restriction base="integer">       <minInclusive value="0"/>       <maxInclusive value="100"/>     </restriction>   </simpleType> </schema>

The first simple type definition, MyInt, defines a datatype to which no additional restrictions were applied. A value of type MyInt can contain any value defined by its base type, int.

The second simple type definition, GenericProductId, defines a restricted version of the string datatype. Values of the type can contain a string from 1 to 20 characters. The Percent definition is similar; it restricts values of the type to integers between 0 and 100.

The XML Schema describes a whole host of constraints (shown in Table 4-3) that can be applied to simple type definitions.

Table 4-3  XML Schema Datatype Constraints 

Constraints

Definition

length

Instances of the type must contain a fixed-length number of units.

minLength

Instances of the type must contain a minimum number of units.

maxLength

Instances of the type can contain only a maximum number of units.

pattern

Instances of the type can contain only data that matches a specific pattern defined by a regular expression. For example, a regular expression that can be used to define a Social Security Number would be [0-9]{3}-[0-9]{2}-[0-9]{4}.

enumeration

Instances of the type can contain only a specified set of values.

whiteSpace

Instances of the type derived from string are processed by the XML parser one of three ways depending on the attribute's value. The default value preserve states that the value is left unchanged. The value replace states that all occurrences of tabs, line feeds, and carriage returns will be replaced with spaces. The value collapse states that any sequence of spaces will be collapsed to a single space.

minInclusive

Instances of the type cannot contain a value less than the value specified. The value of minInclusive must be at least as restrictive as its base type.

minExclusive

Instances of the type cannot contain a value less than or equal to the value specified. The value of minExclusive must be at least as restrictive as its base type.

maxInclusive

Instances of the type cannot contain a value less than the value specified. The value of maxInclusive must be at least as restrictive as its base type.

maxExclusive

Instances of the type cannot contain a value less than or equal to the value specified. The value of maxExclusive must be at least as restrictive as its base type.

totalDigits

Specifies the maximum number of digits an instance of the type can contain. The type must derive from decimal.

fractionDigits

Specifies the maximum number of digits to the right of the decimal point an instance of the type can contain. The type must derive from decimal.

One of the more interesting constraints is pattern. The pattern constraint allows you to define a regular expression that will be used to limit to potential values of a particular type. By leveraging pattern constraints within your simple type definition, you can significantly reduce the amount of validation code you need to write for your Web service.

Let's take a look at an example of where a pattern restriction can be helpful. Recall that the OrderItem method exposed by the Commerce Web service accepts a parameter called Item. In the previous example, I defined a type called ProductId for defining the type of data that can be contained within the Item element.

In addition to length restrictions, suppose that an instance of a ProductId cannot contain the following characters: / \ [ ] : ; = , + * < >. If I were to use the ProductId type as defined previously, I would have to write code to ensure that no illegal characters were included within the Item element. Instead, I will add a pattern constraint to the ProductId definition that restricts the type of characters that values of that type can contain.

<?xml version='1.0'?> <schema xmlns="http://www.w3.org/2001/XMLSchema.xsd"  xmlns:tns="urn:Commerce" targetNamespace="urn:Commerce">   <simpleType name="ProductId">     <restriction base="string">       <minLength value="1"/>       <maxLength value="20"/>       <pattern value='[^/\&#x5B;&#x5D;:; =,+*?&gt;&lt;]+'/>     </restriction>   </simpleType>   <!-- Request Message (work-in-progress) -->   <element name='Item' type='tns:ProductId'/>   <!-- Response Message (work-in-progress) -->   <element name='Amount' type='double' nillable='true'/> </schema>

Another useful constraint is the enumeration. The value of an enumeration can contain one of a fixed set of possible values. For example, suppose I want to restrict the value of the Item attribute to one of a set of possible values. The following example creates a datatype called Items that can contain the possible values of the Item element:

<?xml version='1.0'?> <schema xmlns="http://www.w3.org/2001/XMLSchema.xsd"  xmlns:tns="urn:Commerce" targetNamespace="urn:Commerce">   <!-- Removed the ProductId type definition for clarity -->   <simpleType name="Items">     <restriction base="ProductId">       <enumeration value="Apple"/>       <enumeration value="Banana"/>       <enumeration value="Orange"/>     </restriction>   </simpleType>   <!-- Request Message (work-in-progress) -->   <element name='Item' type='tns:Items'/>   <!-- Response Message (work-in-progress) -->   <element name='Amount' type='double' nillable='true'/> </schema>

The Items type definition creates an enumeration of type ProductId with three possible values. The Item element is defined as type Items, so it can contain only the value Apple, Banana, or Orange.

Simple types can also derive by list. Deriving by list indicates that the value of the type can contain one or more values of the base type, where each value is delimited by whitespace. An example is the SOAP encodingStyle attribute. Recall that this attribute can accept a whitespace-delimited list of URIs. The following example defines the SOAP encodingStyle attribute:

<simpleType name='encodingStyle'>   <list base='uri-reference'/> </simpleType>

List types are not a substitute for SOAP encoded arrays. SOAP arrays provide a standard method of encoding for instances of simple types as well as complex types. SOAP Encoding also defines syntax for the partial serialization of arrays.

Simple types can also be derived by union. An instance of a type derived by union can contain a value of one of the types contained within the union. The following example defines two unions, MyUnion and PhoneNumber:

<?xml version='1.0'?> <schema xmlns="http://www.w3.org/2001/XMLSchema.xsd">   <simpleType name="MyUnion">     <union memberTypes="string int"/>   </simpleType>   <simpleType name="PhoneNumber">     <union>       <simpleType name="UsPhoneNumber"/>         <restriction base="string"/>           <pattern value="([0-9]{3}) [0-9]{3}-[0-9]{4}"/>         </restriction>       </simpleType>       <simpleType name="UkPhoneNumber">         <restriction base="string">           <pattern value="+[0-9]{2} ([0-9])[0-9]{3} [0-9]{3} [0-9]{4}"/>         </restriction>       </simpleType>     </union>   </simpleType> </schema>

The preceding schema shows two ways of defining union simple types. The first type definition uses the memberTypes attribute to list the types contained within the union. The MyElement element can contain string or int values. The second type definition defines a union composed of embedded simple type definitions. The two embedded types define a U.S. phone number and a U.K. phone number. The PhoneNumber union can contain values such as (303) 555-1212 or +44 (0)121 643 2345.

Type definitions can be either named or anonymous. If a type definition is embedded within another definition (an element definition, for example), you do not have to provide the type with a name. Here is a modified version of the Commerce Web service schema that defines the enumeration as an anonymous type:

<?xml version='1.0'?> <schema xmlns="http://www.w3.org/2001/XMLSchema.xsd"  xmlns:tns="urn:Commerce" targetNamespace="urn:Commerce">   <!-- Portions of the schema have been removed for clarity. -->   <!-- Request Message (work-in-progress) -->   <element name='Item'>     <simpleType>       <restriction base="ProductId">         <enumeration value="Apple"/>         <enumeration value="Banana"/>         <enumeration value="Orange"/>       </restriction>     </simpleType>   </element> </schema>

The enumeration containing possible values for the Item element is defined as an anonymous type. Because the enumeration is defined within the scope of the element definition, the type attribute does not need to be specified because it is implied. Because the enumeration type can be referenced only by the element itself, it is not necessary to specify a name for the type.

You should define anonymous types with caution. Types that are used only once are good candidates for anonymous type definitions. However, if the datatype might be reused in other contexts, you should avoid declaring anonymous type definitions.

You should also be cautious about using simple types, including built-in types, within RPC-style Web services. Parameters that are passed by value can be defined using simple types. But parameters passed by reference should not. Recall that SOAP Encoding specifies a means of encoding parameters passed by reference using the id and href attributes. Because elements defined using simple types cannot contain attributes, they cannot be properly encoded within the SOAP message. For this reason, the SOAP Encoding schema defines wrapper types for the built-in types defined by XML Schema.

Complex Types

A complex type is a logical grouping of element and/or attribute declarations. One can argue that XML instance documents aren't very interesting or useful without complex types. For example, the SOAP Envelope schema defines numerous complex types. The Envelope itself is a complex type because it must contain other elements such as the Body element and possibly a Header element. I will use complex types to define the body of the response and request SOAP messages for the Commerce Web service.

A complex type is defined using the complexType element. The complexType element contains declarations for all elements and attributes that can be contained within the element. For example, the body of the PurchaseItem request and response messages can be described by creating a complex type. Here is the schema definition for the Commerce Web service:

<?xml version='1.0'?> <schema xmlns="http://www.w3.org/2001/XMLSchema.xsd"  xmlns:tns="urn:Commerce" targetNamespace="urn:Commerce">   <!-- Type Definitions -->   <simpleType name="ProductId">     <restriction base="string">       <minLength value="1"/>       <maxLength value="20"/>       <pattern value='[^/\&#x5B;&#x5D;:; =,+*?&gt;&lt;]+'/>     </restriction>   </simpleType>   <simpleType name="Items">     <restriction base="ProductId">       <enumeration value="Apple"/>       <enumeration value="Banana"/>       <enumeration value="Orange"/>     </restriction>   </simpleType>   <!-- Request Message (work-in-progress) -->   <element name='PurchaseItem'>     <complexType>       <element name='Item' type='tns:ProductId'/>       <element name='Quantity' type='int'/>     </complexType>   </element>   <!-- Response Message (work-in-progress) -->   <element name='PurchaseItemResponse'>     <complexType>       <element name='Amount' type='double' nillable='true'/>     </complexType>   </element> </schema>

The schema defines two complex types that define the body of the SOAP request and response message. In accordance with the SOAP specification, I defined a PurchaseItem element to contain all of the parameters passed to the PurchaseItem method of the Commerce Web service. The body of the response message will contain an element named PurchaseItemResponse and will contain one subelement for the return type.

Complex types can be divided into two categories: types that contain other elements and types that do not. Within a complex type definition, you can specify either a complexContent or a simpleContent element. The previous datatype definitions did not contain either of these elements. If neither element is used in the complex type definition, complexContent is assumed. Therefore, the following more verbose definition of the PurchaseItem element is equivalent to the previous definition:

<?xml version='1.0'?> <schema xmlns="http://www.w3.org/2001/XMLSchema.xsd"  xmlns:tns="urn:Commerce" targetNamespace="urn:Commerce">   <!-- Portions of the schema have been removed for clarity. -->   <!-- Request Message (work-in-progress) -->   <element name='PurchaseItem'>     <complexType>       <complexContent>         <extension>           <element name='Item' type='tns:ProductId'/>           <element name='Quantity' type='int'/>         </extension>       </complexContent>     </complexType>   </element> </schema>

Notice that the schema also includes the extension element. If either simpleContent or complexContent is specified, its immediate child element must be either the restriction or extension element. By default, a complex type will define an extended version of its base type. If the base type is not specified, the type definition will extend anyType. In other words, a complex type definition that does not explicitly state whether it contains complex or simple content will default to containing complex content and deriving from anyType by extension.

As with simple type definitions, you can create complex types that are more restrictive than the base type. Unlike simple types, which restrict the string value of an instance of a type, complex types have restrictions related to the element and attribute definitions contained within the type. The following example defines the Family complex type and then defines some types that derive by restriction:

<?xml version='1.0'?> <schema xmlns='http://www.w3.org/2001/XMLSchema'>   <!-- Base type -->   <complexType name='Family'>     <element name='Parent' minOccurs='1' maxOccurs='2'/>     <element name='Child' type='string' minOccurs='0'/>   <complexType name='Children'>   <!-- The number of parents is restricted to one. -->   <complexType name='SingleParentFamily'>     <complexContent>       <restriction base='Family'>         <element name='Parent' type='string' minOccurs='1' maxOccurs='1'/>         <element name='Child' type='string' minOccurs='0'/>       </restriction>     </complexContent>   </complexType>   <!-- No Child elements are allowed. -->   <complexType name='ChildlessFamily'>     <complexContent>       <restriction base='Family'>         <element name='Parent' type='string' minOccurs='1' maxOccurs='1'/>         <element name='Child' type='string' minOccurs='0' maxOccurs='0'/>       </restriction>     </complexContent>   </complexType>   <!-- The name of the children can only be George. -->   <complexType name='ForemanFamily'>     <complexContent>       <restriction base='Family'>         <element name='Parent' type='string' minOccurs='0' maxOccurs='2'/>         <element name='Child' type='string' minOccurs='0' fixed='George'/>       </restriction>     </complexContent>   </complexType>   <!-- Not a legal type declaration -->   <complexType name='OrphanedFamily'>     <complexContent>       <restriction base='Family'>         <element name='Parent' type='string' minOccurs='0' maxOccurs='0'/>         <element name='Child' type='string' minOccurs='0'/>       </restriction>     </complexContent>   </complexType> </schema>

I defined three valid restricted derivatives of the Family type. The SingleParentFamily datatype restricts the number of Parent elements that can appear within an instance. The ChildlessFamily datatype disallows the optional Child element from appearing within an instance. Then, in true George Foreman fashion, the ForemanFamily datatype allows Child elements as long as the name of each is George.

One caveat with restricted types—and with extended types, for that matter—is that the derived types must be able to be substituted for their base type without any issue. The two derived types in the example, SingleParentFamily and ForemanFamily, meet this requirement. The OrphanedFamily type definition does not meet this requirement. Because the base type Family states that you must have at least one Parent element, an instance of OrphanedFamily cannot serve as a substitute.

Recall that SOAP Encoding provides a means of maintaining the identity of parameters passed by reference. This is accomplished with the id and href attributes. These attributes allow an element to reference data that is encoded at another location within or even outside of the SOAP message. (See Chapter 3 for more information.) The following example illustrates the need for such a mechanism:

// Server Code: public void TestReference(ref int x, ref int y) {     x += 3;     y += 10; } // Client Code: int z = 2; TestReference(ref z, ref z); // z should now equal 20 (2 + 3 + 10).

For the TestReference method to run correctly, the identity of z must be maintained. Therefore, the elements for parameters x and y cannot be of type int defined by XML Schema because the elements will not be able to contain the href and id attributes to be defined. So, the SOAP Encoding schema extends the built-in types. The following example performs the same redefinition:

<?xml version='1.0'?> <schema xmlns="http://www.w3.org/2001/XMLSchema.xsd"  targetNamespace='urn:ExtendedBuiltinTypes'>   <complexType name='int'>     <simpleContent>       <extension base='int'>         <attribute name='id' type='ID'/>         <attribute name='href' type='uriReference'/>       </extension>     <simpleContent>   </complexType> </schema>

SOAP Encoding specifies that the order in which parameters of an RPC-style message appear is significant. Therefore, I use the sequence element in the schema to indicate that the Item element must appear first, followed by the Quantity element. You can also specify any combination of the minOccurs and maxOccurs attributes. In this case, neither attribute was specified, so the default value of 1 will be assumed. The following is the complete schema for the Commerce Web service:

<?xml version='1.0'?> <schema xmlns="http://www.w3.org/2001/XMLSchema.xsd"  xmlns:tns="urn:Commerce" targetNamespace="urn:Commerce">   <!-- Type Definitions -->   <simpleType name="ProductId">     <restriction base="string">       <minLength value="1"/>       <maxLength value="20"/>       <pattern value='[^/\&#x5B;&#x5D;:; =,+*?&gt;&lt;]+'/>     </restriction>   </simpleType>   <simpleType name="Items">     <restriction base="ProductId">       <enumeration value="Apple"/>       <enumeration value="Banana"/>       <enumeration value="Orange"/>     </restriction>   </simpleType>   <!-- Request Message (work-in-progress) -->   <element name='PurchaseItem'>     <complexType>       <sequence>         <element name='Item' type='tns:ProductId'/>         <element name='Quantity' type='int'/>       </sequence>     </complexType>   </element>   <!-- Response Message (work-in-progress) -->   <element name='PurchaseItemResponse'>     <complexType>       <element name='Amount' type='double' nillable='true'/>     </complexType>   </element> </schema>

Other elements that can be used to achieve specific behavior related to the elements defined within a type include the choice and all elements. The choice element allows only one of the elements defined within the complex type to appear within an instance of the type. The all element allows any subset of the elements defined within the type to appear in any order.

There is one more difference between the all element and the sequence and choice elements. Complex type declarations made within the latter elements can contain maxOccurs and minOccurs attributes. However, elements defined within the all element can specify only a maxOccurs and a minOccurs attribute with a value of 0 or 1.

By default, datatypes defined using the complexContent element do not allow mixed content. Mixed content means values that contain text as well as child elements. You can override this behavior by adding a mixed attribute and setting its value to true. In most cases, including this one, disallowing mixed content is preferred.

Sometimes it is necessary to specify that any element or attribute can appear within an instance of a complex type. For example, the anyType type indicates that any element or attribute can appear within an element of type anyType. You can do this by using the any and anyAttribute elements within the complex type definition. Here is the definition of the anyType type:

<?xml version='1.0'?> <schema xmlns='http://www.w3.org/2001/XMLSchema'  xmlns:tns='http://schemas.xmlsoap.org/soap/envelope/'  targetNamespace='http://schemas.xmlsoap.org/soap/envelope/'>         <xs:complexType name="anyType" mixed="true">     <xs:annotation>       <xs:documentation>       Not the real urType, but as close an approximation as we can       get in the XML representation</xs:documentation>     </xs:annotation>     <xs:sequence>       <xs:any minOccurs="0" maxOccurs="unbounded"/>     </xs:sequence>     <xs:anyAttribute/>   </xs:complexType> </schema>

The preceding portion of the schema for XML Schema itself defines the anyType complex type. The any element states that any element can appear within an element of type anyType. The minOccurs and maxOccurs attributes are also used to indicate that zero or more elements can appear.

You can also impose additional constraints on the attributes and elements that can appear within an instance document by using the namespace attribute. This attribute allows you to declare what namespace-scoped attributes and elements can and cannot be contained within an instance document. Table 4-4 lists the possible values of the namespace attribute.

Table 4-4  Values of the namespace Attribute

namespace Attribute

Description

##any (default)

The parent element can contain any well-formed XML element/attribute from any namespace.

##local

The parent element can contain any well-formed XML element/attribute that does not belong to a namespace.

##targetNamespace

The parent element can contain any well-formed element/attribute that is defined within the schema's target namespace where the type is being defined.

##other

The parent element can contain any well-formed element/attribute not defined within the schema's target namespace where the type is being defined.

Space-delimited list of URIs

The parent element can contain any well-formed element/attribute from the specified namespaces.

The other attribute that can be specified in either the any or anyAttribute element is processContents. The processContents attribute indicates how the instance document should be processed by the system. Table 4-5 lists the possible values of the processContents attribute.

Table 4-5  Values of the processContents Attribute

processContents Attribute

Description

strict (default)

The system must validate all elements/attributes against their respective namespaces.

skip

The system must attempt to validate all elements/attributes against their respective namespaces. If the attempt fails, no errors will be generated.

lax

The system will not attempt to validate elements/attributes against their respective namespaces.

Element and Attribute Groups

You might often find yourself adding the same set of attributes or elements to multiple complex type definitions. The XML Schema provides the group and attributeGroup elements for logically grouping elements and attributes together. Attribute and element groups provide a convenient way to define a set of attributes or elements once and then reference them multiple times in complex type definitions.

One example in which an attribute group is used is within the SOAP Encoding schema. The schema contains complex type definitions that extend the XML Schema built-in types so they can be passed by reference within the body of a SOAP message. Here is an attribute group definition defined by the SOAP Encoding schema:

<attributeGroup name='commonAttributes'>   <attribute name='id' type='ID'/>   <attribute name='href' type='uriReference'/>   <anyAttribute namespace='##other'/> </attributeGroup>

The preceding fragment defines the commonAttributes attribute group. It contains the attribute definition for the id and href attributes, which are necessary for encoding parameters passed by reference. Here is a type definition that derives from the built-in string data type that references the attribute group:

<element name='string' type='tns:string'/> <complexType name='string'>   <complexContent>     <extension base='string'>       <attributeGroup ref='tns:commonAttributes'/>     </extension>   </complexContent> </complexType>

The above type definition is actually an updated version of the one that appears in the SOAP Encoding schema. The original schema was written against a previous version of the XML Schema specification. The complex type definition references the attribute group using the ref attribute, which contains the value of the targeted attribute group definition. Elements can be grouped together using the group element and can be referenced using the ref attribute as well.

Namespace Scoping

Element and attribute declarations that are locally scoped within a complex type definition can be either qualified or unqualified. The default value is unqualified, which means that the element or attribute is not affiliated with any namespace. Here is an example:

<?xml version='1.0'?> <schema xmlns='http://www.w3.org/2001/XMLSchema'  xmlns:tns='urn:Example:Scoping' targetNamespace='urn:Example:Scoping'>   <element name='GloballyScoped'/>   <element name='MyElement'>     <complexType>       <element name='LocallyScoped'/>       <element ref='tns:GloballyScoped'/>     </complexType>   </element> </schema>

The schema defines two globally scoped elements, GloballyScoped and MyElement. The MyElement element declaration defines an anonymous complex type that contains two element declarations: a locally scoped element named LocallyScoped and a reference to a globally scoped element named—what else—GloballyScoped. Next I'll create an instance document:

<?xml version='1.0'?> <ex:MyElement xmlns:ex='urn:Example:Scoping'>   <LocallyScoped/>   <ex:GloballyScoped/> </ex:MyElement>

The instance document contains a single MyElement element. Notice that the child elements of the MyElement element are qualified differently. The LocallyScoped element does not have a prefix because it is not affiliated with any namespace. The GloballyScoped element is fully qualified within the ex: prefix. Because the GloballyScoped element was defined as a global element, it is affiliated with the urn:Example:Scoping namespace.

Be aware that, if you set the default namespace within an instance document, locally scoped elements and attributes do not belong to the default namespace. For example, the following instance document is not valid because the LocallyScoped element is not a part of the urn:Example:Scoping namespace:

<?xml version='1.0'?> <MyElement xmlns='urn:Example:Scoping'>   <!-- Invalid because LocallyScoped is not affiliated    with the default namespace -->   <LocallyScoped/>   <GloballyScoped/> </MyElement>

There are two ways to avoid this problem. The first solution is to assign a prefix to the namespace reference instead of assigning a default namespace. In the first example, I associated the ex: prefix with the urn:Example:Scoping namespace. The second solution is to override the default namespace declaration in each local element or attribute, as in this example:

<?xml version='1.0'?> <MyElement xmlns='urn:Example:Scoping'>   <!-- Valid because LocallyScoped overrides the default namespace -->   <LocallyScoped xmlns=''/>   <GloballyScoped/> </MyElement>

You can avoid problems with locally scoped elements and attributes by affiliating them with the namespace in which they are defined. You can do this by setting the form attribute within the element or attribute declaration to qualified. This requires the locally scoped element or attribute to be qualified with respect to its namespace. Here is an updated version of the schema:

<?xml version='1.0'?> <schema xmlns='http://www.w3.org/2001/XMLSchema'  xmlns:tns='urn:Example:Scoping2' targetNamespace='urn:Example:Scoping2'>   <element name='GloballyScoped'/>   <element name='MyElement'>     <complexType>       <element name='LocallyScoped' form='qualified'/>       <element ref='tns:GloballyScoped'/>     </complexType>   </element> </schema>

This time, I indicated that the LocallyScoped element must be fully qualified within the instance document. Here is the instance document updated to reflect the changes made to the schema:

<?xml version='1.0'?> <MyElement xmlns='urn:Example:Scoping2'>   <!-- Valid since LocallyScoped element must be fully qualified -->   <LocallyScoped/>   <GloballyScoped/> </MyElement>

You can also override the default value for the form attribute. You can do this by setting two attributes in the schema element: elementFormDefault and attributeFormDefault. Schemas automatically generated by the .NET platform for Web services will generally set elementFormDefault and attributeFormDefault to qualify.

Polymorphism

Polymorphism is when instances of different types can be treated similarly. The XML Schema provides two mechanisms for enabling polymorphic behavior: inheritance and substitution groups.

As I demonstrated in the previous sections, XML Schema provides a rich inheritance model. You can create new simple types that derive by restriction, and you can create new complex types that derive by extension as well as restriction.

One of the rules of a derived type is that it must be able to be substituted for its base type. As a result, an instance of a derived type can be substituted in an instance document for its base type. The system is informed that the instance document contains an instance of a derived type via the xsi:type attribute.

For example, suppose you want to create a common type system for describing tires. You want any tire dealer or manufacturer to be able to use this type system to create a Web service for obtaining price quotes for the tires they sell. Here are the common datatypes used to describe tires:

<?xml version='1.0'?> <schema xmlns='http://www.w3.org/2001/XMLSchema'  targetNamespace='urn:TireTypes'>   <complexType name='Tire' abstract='true'>     <element name='WheelDiameter' type='int'/>     <element name='Width' type='int'/>   </complexType>   <complexType name='AutoTire'>     <complexContent>       <extension base='Tire'>         <element name='WheelDiameter' type='int'/>         <element name='Width' type='int'/>         <element name='AspectRatio' type='int'/>       </extension>     </complexContent>   </complexType>   <element name='MountainBikeTire'>     <complexContent>       <extension base='Tire'>         <element name='WheelDiameter' type='int'/>         <element name='Width' type='int'/>         <element name='Position'/>           <simpleType>             <restriction base='string'>               <enumeration value='Front'/>               <enumeration value='Rear'/>             </restriction>           </simpleType>         </element>       </extension>     </complexContent>   </element> </schema>

This schema defines the Tire base type. It contains two child elements for the rim size and the width of the tire. It then derives two separate types from the Tire base type called AutoTire and MountainBikeTire. In both instances, the Tire type is extended to add additional elements needed to describe the specific type of tire.

Instances of the Tire base type include insufficient information to describe a specific tire. Therefore, the abstract property within the type declaration is set to true. Setting the abstract property of the Tire complex type definition to true indicates that the Tire type is not intended to be directly creatable.

A fictitious company, The Round Rubber Tire Company, sells all types of tires and wants to expose a Web service for getting price quotes on tires. Here is a schema for the NewTires Web service that leverages the tire types:

<?xml version='1.0'?> <schema xmlns='http://www.w3.org/2001/XMLSchema' xmlns:vt='urn:TireTypes'  targetNamespace='http://roundrubbertire.com/NewTires'  elementFormDefault='qualified'>   <element name='GetQuote'>     <complexType>       <element name='Tire' type='vt:Tire'/>       <element name='Quantity' type='int'/>     </complexType>   </element>   <element name='GetQuoteResults'>     <complexType>       <element name='Result' type='double'/>     </complexType>   </element> </schema>

The GetQuote method accepts information about the requested tire and the quantity. The price of the new tires is then returned as a double. However, because the Tire datatype is abstract, the Web service needs to receive a derivative of the Tire type. The following SOAP message requests a quote for new tires of type AutoTire:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:tires="http://bigrubbertire.com/NewTires" xmlns:vt="urn:TireTypes">   <soap:Body>     <tires:GetQuote>       <tires:Tire xsi:type="vt:AutoTire"/>         <tires:WheelDiameter>16</tires:WheelDiameter>         <tires:Width>225</tires:Tires>         <tires:AspectRatio>50</tires:AspectRatio>       </tires:Tire>       </tires:Quantity>4</tires:Quantity>     </tires:GetQuote>   </soap:Body> </soap:Envelope>

The body of the SOAP message contains the GetQuote element that contains the Tire parameter. The Tire parameter contains an instance of the AutoTire type, as indicated by the xsi:type attribute. The parameter is a legal substitution because AutoTire is a derivative of Tire.

XML Schema also supports polymorphic behavior at the element level via the concept of substitution groups. A substitution group is a group of elements that can serve as substitutes for a given element within an instance document. You can add element definitions to a substitution group by using the substitutionGroup attribute.

The substitutionGroup attribute contains a reference to the element for which it can serve as a substitute. All element definitions within a substitution group must be the same type or a derivative of the type of the target element. In the following example, the schema for the NewTires Web service is rewritten to use group substitution instead of type substitution:

<?xml version='1.0'?> <schema xmlns='http://www.w3.org/2001/XMLSchema' xmlns:vt='urn:TireTypes'  targetNamespace='http://rubbertire.com/NewTires'  xmlns:tns='http://rubbertire.com/NewTires' elementFormDefault='qualified'>   <element name='GetQuote'>     <complexType>       <element ref='tns:Tire'/>       <element name='Quantity' type='int'/>     </complexType>   </element>   <element name='GetQuoteResults'>     <complexType>       <element name='Result' type='double'/>     </complexType>   </element>   <!-- Declare the Tire element and its substitutes. -->   <element name='Tire' type='vt:Tire' abstract='true'/>   <element name='AutoTire' type='vt:AutoTire' substitutionGroup='tns:Tire'/>   <element name='MountainBikeTire type='vt:MountainBikeTire'    substitutionGroup='tns:Tire'/> </schema>

In the new schema, the definition of the Tire element was moved from within the GetQuote complex type (locally scoped) to directly under the schema element (globally scoped). Because I did not want the tire element to appear within the instance document, I set the abstract property to true within the element definition. I then defined two other elements to serve as substitutions for the Tire element. Here is the resulting SOAP message for ordering a set of automobile tires:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tires="http://bigrubbertire.com/NewTires">   <soap:Body>     <tires:GetQuote>       <tires:AutoTire/>         <tires:WheelDiameter>16</tires:WheelDiameter>         <tires:Width>225</tires:Tires>         <tires:AspectRatio>50</tires:AspectRatio>       </tires:AutoTire>       <tires:Quantity>4</tires:Quantity>     </tires:GetQuote>   </soap:Body> </soap:Envelope>

The Tire element was replaced by the AutoTire element within the document. There was no need to decorate the element with the xsi:type attribute because the element is strongly typed by the schema itself.

Restricting Inheritance

Because any derived type can be substituted for its base type, you might sometimes want to state how a base class can be inherited. For example, the urn:TireTypes namespace defined earlier defines the Tire datatype. The Tire datatype is defined as abstract because an instance of that type would not contain enough information to adequately describe a tire. However, setting the type to abstract does not provide a full solution.

A client can easily circumvent using a more rich type by deriving a new type from Tire by restriction. The client can then invoke the GetQuote method and pass it an instance of the new type. Here is an example:

<?xml version='1.0'?> <schema xmlns='http://www.w3.org/2001/XMLSchema' xmlns:tire='urn:TireTypes'  targetNamespace='urn:DerivedTireTypes'>   <complexType name='SkinnyTire' abstract='true'/>     <complexContent base='tire:Tire'>       <restriction >         <element name='WheelDiameter' type='int'/>         <element name='Width' type='int' fixed='1'/>       </restriction>     </complexContent>   </complexType> </schema>

I first derived a more restricted version of the Tire type. I'll then pass an instance of this new type to the GetQuote method:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tires="http://bigrubbertire.com/NewTires"  xmlns:vt="urn:DerivedTireTypes"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">   <soap:Body>     <tires:GetQuote>       <tires:Tire xsi:type="dt:SkinnyTire"/>         <tires:WheelDiameter>16</tires:WheelDiameter>         <tires:Width>1</tires:Tires>       </tires:Tire>       <tires:Quantity>2</tires:Quantity>     </tires:GetQuote>   </soap:Body> </soap:Envelope>

As I stated earlier, the Web service cannot quote the price of a tire based on only the wheel diameter and width. Therefore, if the Web service receives an instance of SkinnyTire, a restricted derivation of the Tire datatype, it will be unable to provide a price quote. One solution is to disallow inheritance by restriction.

An example of where you might want to disallow inheritance by extension is if you use a type that represents the long version of the U.S. Federal income tax form. It might not be necessary for all filers to complete the entire long form, so the government issues the EZ form. The EZ form is a derivative of the long form with restrictions on the amount of data it can contain.

You can dictate how a datatype can be inherited by setting the final attribute in the complexType element. Table 4-6 describes the possible values.

Table 4-6  Values of the final Attribute

final Attribute

Description

#all

The type cannot serve as a base type for types that are derived by extension and restriction.

restriction

The type can be a base type only for types that derive by extension.

extension

The type can be a base type only for types that derive by restriction.

If the final attribute is not set, the default value is #all. You can override the default value by setting the finalDefault attribute within the schema element.

Sometimes it makes sense to allow others to inherit from datatypes but limit instances of derived types from appearing in instance documents. For example, tax preparation services often collect information beyond what is called for on the long income tax form. The tax preparation service might therefore want to derive from the long form datatype by extension for use within its own internal system.

When it comes time to electronically file the tax form, the schema for the Web service needs a means of disallowing instances of the extended versions of the long form datatype. This is accomplished by setting the block attribute on the tax form element declaration to extension. The other possible values of the block attribute are listed in Table 4-7.

Table 4-7  Values of the block Attribute

block Attribute

Description

restriction

The element cannot contain an instance of a type derived by restriction.

extension

The element cannot contain an instance of a type derived by extension.

substitution

The element cannot be substituted for another element within its substitution group.

#all

The element cannot contain an instance of a derived type and cannot be substituted for another element within its substitution group.

If the block attribute is not specified, the default behavior is to allow the element to contain an instance of a derived type or be substituted with another element within its substitution group. You can override the default value by setting the finalDefault attribute within the schema element to the desired value.



Building XML Web Services for the Microsoft  .NET Platform
Building XML Web Services for the Microsoft .NET Platform
ISBN: 0735614067
EAN: 2147483647
Year: 2002
Pages: 94
Authors: Scott Short

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net