An instance of a type derived by union can contain a value of one of the types contained within the union.. As with simple type definitions, you can create complex types that are more re
Trang 1leveraging 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
<! Request Message (work-in-progress) >
<element name=‘Item’ type=‘tns:ProductId’/>
<! Respons e 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:
Trang 2<! 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
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:
Trang 3The 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
<! Portions of the schema have been removed for clarity >
<! Request Message (work-in-progress) >
Trang 4The 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 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
RPC-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:
Trang 5<element name=‘Item’ type=‘tns:ProductId’/>
<element name=‘Quantity’ type=‘i nt’/>
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>
Trang 6<extension>
<element name=‘Item’ type=‘tns:ProductId ’/>
<element name=‘Quantity’ type=‘int’/>
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’/>
<! No Child elements are allowed >
<complexType name=‘ChildlessFami ly’>
Trang 7I 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
Trang 8the 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:
// 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:
<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:
Trang 9<element name=‘Item’ type=‘tns:ProductId’/>
<element name=‘Quantity’ type=‘int’/>
Trang 10Other 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
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
Trang 11Table 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
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’/>
Trang 12<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’/>
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:
The schema defines two globally scoped elements, GloballyScoped and MyElement The
MyElement element declaration defines an anonymous complex type that contains two
Trang 13element 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
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:
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’
Trang 14This 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:
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:
Trang 15<?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’/>
<element name=‘WheelDiameter’ type=‘int’/>
<element name=‘Width’ type=‘int’/>
Trang 16AutoTire 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:
<element name=‘Tire’ type=‘vt:Tire’/>
<element name=‘Quantity’ type=‘int’/>
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:
Trang 17The 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:
<! Declare the Tire element and its substitutes >
<element name=‘Tire’ type=‘vt:Tire’ abstract=‘true’/>
Trang 18<element name=‘AutoTire’ type=‘vt:AutoTire’
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:
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’
Trang 19targetNamespace=‘urn:DerivedTireTypes’>
<complexType name=‘SkinnyTire’ abstract=‘true’/>
<complexContent base=‘tire:Tire’>
<restriction >
<element name=‘WheelDiameter’ type=‘int’/>
<element name=‘Width’ type=‘int’ fixed=‘1’/>
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