Writing a Schema Explicitly


A schema is just like any other XML document—it has a root element, it should be well formed and valid, and so on—but a schema’s purpose is unique. You are rigidly defining the contents of a namespace in XML—that is, the valid elements in the namespace and the types of those elements. You can define elements that contain text or have attributes and are considered to be of a simple type. You can define elements that contain other elements and are considered to be of a complex type. Either way, you have to define the elements and their types.

A single schema typically defines all the contents of a namespace, but several schemas can work together for the same purpose, much as multiple cascading style sheets can define the style of an HTML page.

XML documents written to obey schemas are called schema instance documents. SOAP messages are fine examples of schema instance documents—they need the SOAP schema to validate the SOAP message framework, and they need your schema to validate the message payload. As the name suggests, the relationship between schema and instance document is comparable to that of class and object. One is an instance of the other.

The Root <schema> Element

At the root of all schemas is the <schema> element. It has several attributes that you can use to “reference” other schemas and define the shape of the instance documents that will follow the schema. Every type and element definition you write for the schema will be a descendent of this element. The following code illustrates the use of the <schema> element:

<xs:schema targetNamespace="http://www.notashop.com/wscr"   elementFormDefault="qualified"    attributeFormDefault="qualified"   xmlns:tns="http://www.notashop.com/wscr"   xmlns:xs="http://www.w3.org/2001/XMLSchema"   >   <!-- all type and element definitions go here --> </xs:schema>

The key tasks in this code are to identify the namespace whose contents the schema is defining—by using the targetNamespace attribute—and to reference your new types and elements within the schema—by using the xmlns attribute to set a prefix for the namespace. By convention, this prefix is usually tns—short for target namespace. You can also add references to any other schemas that yours will use, by using xmlns as you would in any other XML document. For example, to use the built-in xsd types, you include the line xmlns:xs="http://www.w3.org/2001/ XMLSchema”. Note that xs and xsd are the usual prefixes for the XSD schema. Table 6-1 describes the two namespaces most commonly used with schemas.

Table 6-1: Schema and Schema Instance Namespace URLs

Namespace URL

Description

http://www.w3.org/2001/XMLSchema

References all the possible constructs in a schema document

http://www.w3.org/2001/XMLSchema-instance

References all the schema constructs allowed in a schema instance document

With our namespaces defined in the schema, we can also specify whether the elements and attributes in our schema’s instance documents should be qualified with a prefix, by using the elementFormDefault and attributeFormDefault attributes of the <schema> element—that is, we specify whether the elements and attributes will be part of a namespace other than the default one. By default, elementFormDefault and attributeFormDefault are both set to unqualified, which means they will not. Setting them to qualified, as in our example, means they will. Note that you can override this default setting on a per-element or per-attribute basis by using their form attribute. (More on this later.)

Tip

.NET strays from the schema standard in the default setting for the elementFormDefault attribute. The schema generator used by the .NET Web services framework sets elementFormDefault to qualified by default.

Chapter 5 noted that qualifying every element and doing away with the default namespace in an XML document is a good thing. This is doubly true when you work with schemas, for two reasons:

  • Everything declared in a schema is either part of the given targetNamespace or is not part of any namespace. Qualifying your declarations should remind you of this.

  • You risk some troublesome scoping issues when you define elements that contain other elements. We’ll discuss them later when we come to complex types, but these risks are removed if you do not rely on a default namespace.

Associating Schemas with Instance Documents

Before we start defining types and elements, let’s consider a couple of other questions posed in Chapter 5: How do we associate an instance document with its schema? And how do we find the actual schema for a given namespace?

Annoyingly, there is no standard way of doing either, but typically the first problem is tackled programmatically at run time. For example, you’ve seen that we add schemas to an XmlValidatingReader object’s Schemas property for the instance document to be validated against. The only trick is to find the actual schema document. A few schemas can be found at the location given by the namespace URL they represent—the SOAP schema is a good example. Others can be found by checking the instance document for an xsi:schemaLocation attribute, as in this example:

<a:AddRecord xmlns:a="http://www.notashop.com/wscr"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://www.notashop.com/wscr/AlbumEntry.xsd">   <a:album>Devin Townsend</a:album>   <a:artist>Accelerated Evolution</a:artist> </a:AddRecord>

The schemaLocation attribute is a whitespace-delimited list of namespaces in the instance document and the locations of the schemas that govern them. Again, your schema validator might not know what to do with the hints you provide, but as Chapter 5 showed, you have some space in the handler API to remedy this. You can also use an xsi:noNamespaceSchemaLocation attribute to reference schemas with no namespaces, but again you should avoid defining elements without namespaces for reasons we’ve already given.

Declaring Elements

You define new elements in your namespace by using the <element> element in a schema. At a minimum, you supply a name for the element and the type of information it contains. Your new name must follow XML naming conventions, and its type should be one of the 47 XSD types documented in Appendix A or a custom type defined in this schema or another schema. For example

<xs:element type="xs:string" name="album" /> <xs:element type="xs:gYear" name="yearOfRelease" />

Elements declared as children of the <schema> element are considered global declarations and can be used in other schemas and anywhere else in the same schema. If an element is declared as a child of some other part of the schema (usually a type definition), it is considered local to that part and cannot be referenced outside of it. With respect to SOAP messages, your schema should contain definitions for the root elements of both the request messages and the response messages your Web service will use. For instance, have another look at the WSDL file for the first Web service we created in this book, http://localhost/wscr/01/simple1.asmx?wsdl. You’ll find the following:

<s:schema elementFormDefault="qualified"    targetNamespace="http://www.notashop.com/wscr">   <s:element name="WakeUp">        </s:element>   <s:element name="WakeUpResponse">        </s:element> </s:schema>

Don’t forget that you’re writing a schema to reflect the messages you want sent between client and server. Stay true to that.

Adding Restrictions

Once you declare the basic element, you can add the following basic attributes to it, which qualify how it will appear in instance documents that follow your schema:

  • maxOccurs and minOccurs Govern how many times the element can appear in an instance document. By default, both are set to 1, so the element can appear only once.

  • default Specifies a default value for an element, where it is possible to give one.

  • fixed Specifies a fixed value for an element, where it is possible to give one. Note that default and fixed cannot be used together for the same element.

  • form Specifies whether this element must be fully qualified by a namespace URL in an instance document. This is the per-element override for the schema’s elementFormDefault attribute you saw earlier. You can set the form attribute to qualified or unqualified.

Note that maxOccurs and minOccurs can be applied only to local element declarations. Likewise, there’s no need to set form to qualified for the root elements of your instance documents because this is assumed to be the case. Let’s expand on the ideas in Chapter 2 for adding band and album details to a music database:

<xs:element type="xs:gYear" name="yearOfRelease" default="2003" /> <xs:element type="xs:string" name="formerBandName"              minOccurs="0" maxOccurs="2"/>

In this example, <yearOfRelease> defaults to 2003 if it isn’t specified in our album submission. Meanwhile, <formerBandName> is optional, but it can occur up to twice in one instance document to cover any names the band formerly used.

Empty and Nillable Elements

Just as you can define a variable to be empty or null, you can do the same with elements in your schema. An empty element in this case is one that has no text content or child elements, although it can have attributes—for example, <br />. This translates into <xs:element> either not having a type or having a type that has no local elements or text nodes. For example

<xs:element name="ThisIsEmpty" />

If you or your client need to convey the concept of a null value in your SOAP messages, you can identify in your schema which elements might need to be null by setting the nillable attribute of their <element> declaration to true. By default, this attribute is set to false. Therefore, if we want to make it possible but not mandatory to submit an album cover to our database, we can declare the element in this way in the schema:

<xs:element name="albumCover" nillable="true" type="xs:base64binary"/>

If a client doesn’t have a scan of the cover to submit, it can translate the null value for the image into the following:

<a:albumCover xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                xmlns:a="http://www.notashop.com/wscr"                xsi:nil="true" />

The <albumCover> element remains valid even though it doesn’t contain a base64- encoded binary string because xsi:nil is set to true.

Declaring Attributes

You define attributes in much the same way as you do elements, except you use the <attribute> element in your schema. For example

<xs:attribute name="noOfPlayers" type="xs:byte" use="optional" /> <xs:attribute name="nationality" type="xs:string" default="British" />

Like <element>, <attribute> also has name, type, form, default, and final attributes, each with the purpose discussed earlier. maxOccurs and minOccurs are not used, however, because it can be assumed that you’ll be using that attribute either once or not at all. In their place is the use attribute, which can be set to optional, required, or prohibited.

We can also make the same distinction between global and local attribute definitions. Attributes defined as children of the root <schema> element are global and can be referenced by other schemas and any type in the schemas that contains it. You should use the ref attribute rather than name if you want to reference a global attribute from another schema. For example

<xs:schema targetNamespace="http://www.notashop.com/wscr"   xmlns:tns="http://www.notashop.com/wscr"   xmlns:xs="http://www.w3.org/2001/XMLSchema"     xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">   <xs:attribute ref="soap:mustUnderstand" default="1" /> </xs:schema>

An instance document based against your schema can then validly use the <soap:mustUnderstand> attribute as your schema wants.

A local attribute definition is one given as a child of some element other than the root <schema> element, most likely a type definition. Like local element definitions, they are specific to that type and cannot be referenced elsewhere.

Declaring Custom Types

So far, we’ve assumed that the types of the elements and attributes in our SOAP messages all correspond to one of the built-in XSD types, but that won’t always be the case. One of the most powerful aspects of the XSD type system is that it lets you derive custom types from those already defined and reuse those types, much as you do in .NET. Custom types are neatly divided into two categories:

  • Simple types These describe textual data. That is, they can be applied only to text-only elements or attributes. They are derivations of other simple types by restriction, by list, or by union.

  • Complex types These describe structured data. That is, they contain attributes and other elements to create a nested structure in an instance document.

As shown in Appendix A, the built-in XSD types are all simple types derived from one root type called anyType. Each derived type is a restriction of its parent. For example, the definition of the byte type shows that it is just a subset of the values allowed in the short type.

Custom Simple Types

You can create your own simple types as restrictions, lists, and unions of existing simple types. In our music database scenario, this is useful in several cases. For example, we can assume that we won’t be including records issued during or before World War II, so we can create a new type to reflect this:

<xs:schema targetNamespace="http://www.notashop.com/wscr"        xmlns:tns="http://www.notashop.com/wscr"      xmlns:xs="http://www.w3.org/2001/XMLSchema">   <xs:element name="yearOfRelease" type="tns:postWar" />   <xs:simpleType name="postWar">     <xs:restriction base="xs:gYear">         <xs:minExclusive value="1945" />     </xs:restriction>   </xs:simpleType> </xs:schema>

In this case, we’ve defined a type called postwar, which is a restricted version of the gYear data type, denoted by the <restriction> element’s base attribute. Values of this type can only be years from 1946 onward. Note that when we create an element of that type, we reference it as tns:postWar. Don’t forget that we are defining types within a target namespace and that they must be fully qualified in a schema so they aren’t treated as part of another namespace or as having no namespace at all.

In our example, we’ve used the minExclusive constraint to define how the original type will be restricted to create ours. There are actually 12 of these constraints, or facets, as they are properly known, as listed in Table 6-2.

Table 6-2: Schema Simple Type Constraints

Facet

Description

enumeration

Allows you to create a set of literal values that elements of that type might be given. This mirrors the .NET enum construct.

fractionDigits

Sets the maximum number of digits a number can have to the right of the decimal point. Must be equal to or less than the value for totalDigits if both are used in the same restriction.

length

Specifies the number of characters in a string, number of items in a list, or number of octets in a binary type. This facet cannot be used with minLength or maxLength.

maxExclusive

Sets a boundary that values of the new type cannot equal or exceed. maxExclusive must be less than the maximum value of the original type.

maxInclusive

Sets a boundary that values of the new type cannot exceed but can equal. maxInclusive must be less than the maximum value of the original type.

maxLength

Sets an upper limit on the length of a value in the new type. Applies to strings, lists, and binary types.

minExclusive

Sets a boundary that values of the new type cannot equal or be less than. minExclusive must be greater than the minimum value of the original type.

minInclusive

Sets a boundary that values of the new type cannot be less than but can equal. minInclusive must be greater than the minimum value of the original type.

minLength

Sets the lower limit for the length of a value in the new type. Applies to strings, lists, and binary types.

pattern

Introduces a regular expression pattern that values of the new type must match.

totalDigits

Sets the maximum total number of digits a number can have. Must be greater than or equal to the value of fractionDigits if both are used in the same restriction.

whiteSpace

Defines how whitespace characters are treated inside string-based types. This attribute has three possible values:

preserveWhitespace characters remain as written.

replaceAll carriage return, line feed, and tab characters are turned into space characters.

collapseSame as replace, but with all leading and trailing spaces removed and any sequence of two or more spaces reduced to one.

If you take a look at Appendix A, you’ll see that each XSD type entry includes a list of the facets that are applicable to it. You can derive your own simple type by using those facets to restrict the XSD type. As long as the facets are valid for your base type, you can use a combination of them to create your custom type.

Tip

You can specify patterns on types, but you cannot specify a restriction on an element based on the value of another element. This is beyond the capabilities of schemas. Extension projects such as Schematron.NET (http://sourceforge.net/projects/dotnetopensrc), however, allow these additional checks to be made.

From a Web service point of view, it’s easier to serialize custom simple types, which are restrictions of their base types, than types that are unions or lists. Take, for example, an enumerated type:

<xs:element name="albumFormat" type="tns:formatTypes" /> <xs:simpleType name="formatTypes">   <xs:restriction base="xs:string">     <xs:enumeration value="CD" />     <xs:enumeration value="Tape" />     <xs:enumeration value="Vinyl" />     <xs:enumeration value="Minidisc" />   </xs:restriction> </xs:simpleType>

As noted before, this new type would map very well to a .NET enum construct. The <albumFormat> element defined here can take one of the four given values as its text value. However, if the element contains a list of enumerated values, as shown here, we’re a little stuck:

<xs:element name="albumFormats" type="tns:formatList" /> <xs:simpleType name="formatList">   <xs:list itemType="tns:format" /> </xs:simpleType>

Deriving by list means that elements of this new type might contain several values in the base type, separated by some whitespace character. In our example, then, valid values for the element <albumFormats> include:

<x:albumFormats>CD Vinyl</x:albumFormats> <x:albumFormats>Tape Minidisc CD</x:albumFormats>

There is no .NET type that translates directly to or from a list such as this, other than a string. We have the same translation problems if we create a new simple type by union:

<xs:simpleType name="dayAndDate">   <xs:union memberTypes="xs:string xs:date" /> </xs:simpleType>

An element of type tns:dayAndDate is hard to translate into a single .NET variable that can store the same data with the same meaning.

Complex Types

You’ve seen how to define elements, attributes, and simple types. By creating new complex types, you can combine all three and start to build a rich and meaningful set of messages for your Web services. For example, the SOAP schema defines a SOAP <envelope> element to contain a header, a body, and a section following the body that can contain anything at all. It can also contain any attribute defined in a different schema. Here’s a portion of the SOAP Envelope schema:

<xs:element name="Envelope" type="tns:Envelope" /> <xs:complexType name="Envelope" >   <xs:sequence>     <xs:element ref="tns:Header" minOccurs="0" />     <xs:element ref="tns:Body" minOccurs="1" />     <xs:any namespace="##other" minOccurs="0"        maxOccurs="unbounded" processContents="lax" />   </xs:sequence>   <xs:anyAttribute namespace="##other" processContents="lax" /> </xs:complexType>

We’ll come to the wildcard elements, <any> and <anyAttribute>, in a moment, but let’s first turn to the <complexType> element. Every complex type definition in a schema starts with a <complexType> element, which can be given a name if it is a global type declaration to be used elsewhere. Under this element, we can specify one of three ways to construct our complex type:

  • <sequence> Defines the order in which the child elements must appear, if they appear at all

  • <choice> Defines a group of elements from which exactly one might appear in the instance document

  • <all> Denotes that all of the elements defined will appear, but that they might appear in any order as subelements of the complex typed element

Your chosen construction method should be written in as the child element of <complexType> and contain the group of elements that the complex type encompasses. For example, we could define the request message payload element for our album submission Web service as follows:

<xs:element name="albumSubmission" type="tns:albumEntryType" /> <xs:complexType name="albumEntryType">   <xs:sequence>     <xs:element ref="tns:artist" minOccurs="1" />     <xs:element ref="tns:album" minOccurs="1" />     <xs:element name="releaseDate" type="tns:dayAndDate" />   </xs:sequence> </xs:complexType>

Our incoming messages would then all have to look something like this:

<a:albumSubmission xmlns:a="http://www.notashop.com/wscr">   <a:artist>Helmet</a:artist>   <a:album>Meantime</a:album>   <a:releaseDate>June 1991</a:releaseDate> </a:albumSubmission>

Note that we have quite a few options when defining the group of elements within the sequence. We can

  • Reuse a global element by using ref

  • Define a new local element by using name

  • Nest another sequence or choice (but not all) group within the current one

  • Include wildcards in place of elements

As you saw in the definition of the SOAP <envelope> element, you can use <any> as a wildcard to indicate that any element can appear in its place given certain constraints that are expressed as the attributes of <any>, as shown in Table 6-3.

Table 6-3: Wildcard Constraints

Attribute

Default Value

Description

maxOccurs

1

Specifies the maximum number of times elements satisfying the wildcard can appear in an instance of this type in this context. maxOccurs can be set to unbounded or any number greater than or equal to 0.

minOccurs

1

Specifies the minimum number of times elements satisfying the wildcard can appear in an instance of this type in this context. The value for minOccurs must be equal to or less than that of maxOccurs.

namespace

##any

Specifies which namespace the element(s) taking the place of the wildcard must belong to. This attribute has five possible values:

##target-NamespaceElements from the target namespace given in the schema can be used.

##localOnly elements with no namespace can be used.

URIOnly elements from the given namespace URI can be used.

##otherAny elements defined in a namespace other than the schema’s target namespace can be used.

##anyAny element, with or without a namespace, can be used.

processContents

strict

Specifies whether the validator should locate the appropriate schema to validate elements replacing the wildcard. There are three possible values.

laxValidate the elements if the appropriate schema is available; otherwise, don’t worry about them.

skipDon’t try to validate the elements.

strictThe validator must locate the schema for the element and validate it.

If you’ll recall the definition of a SOAP envelope that you saw earlier, we can decode the first wildcard stuck after the SOAP <body> element:

<xs:any namespace="##other" minOccurs="0"          maxOccurs="unbounded" processContents="lax" />

This code states that a SOAP envelope can contain any number of elements after the <body> element as long as they are not elements from the SOAP namespace. If any elements are present in a SOAP message that is to be validated, they will be validated if a schema is at hand; with no schema present, the validator will just carry on as if the message is valid.

In addition to subelements, we can also use our <complexType> definition to attach attributes to an element. If we need to do this, our attribute declarations must follow the <sequence>, <choice>, or <all> construction defined in the complex type. For example, we can add a sender attribute to our album submission to identify who sent us the information:

<xs:complexType name="albumEntryType">   <xs:sequence>        </xs:sequence>   <xs:attribute name="sender" type="tns:emailAddress" use="required" /> </xs:complexType>

There is also a wildcard for attributes that we can add to a complex type called <anyAttribute>. You saw this in action in the SOAP schema. We can control how wild <anyAttribute> is with the namespace and processContents attributes as defined in Table 6-3. minOccurs and maxOccurs do not exist for <anyAttribute>.

Simple, Mixed, and Complex Content in Complex Types

Thus far, we’ve looked solely at complex types that contain only child elements and possibly attributes. However, complex types can also contain a text value. A type’s content can be categorized based on the different combinations of child elements, attributes, and text values that it can have, as follows:

  • Complex content Child elements and possible attributes

  • Simple content Attributes and optionally a text element

  • Mixed content Child elements, a text element, and optionally some attributes

Unless told otherwise, a schema validator assumes that a complex type definition contains complex content. This is not difficult to remember, but if need be, we can make this statement explicit by adding a <complexContent> element around our group of child elements:

<xs:element name="albumSubmission" type="tns:albumEntryType" /> <xs:complexType name="albumEntryType">   <xs:complexContent>     <xs:extension>       <xs:sequence>         <xs:element ref="tns:artist" minOccurs="1" />         <xs:element ref="tns:album" minOccurs="1" />         <xs:element name="releaseDate" type="tns:dayAndDate" />       </xs:sequence>       <xs:attribute name="sender"          type="tns:emailAddress" use="required" />     </xs:extension>   </xs:complexContent> </xs:complexType>

Immediately inside <complexContent>, we must state whether the type is an extension or a restriction of its base type by using <restriction> or <extension> elements—for example, <xs:restriction base="xs:albumInfo" />. By default, the type is assumed to be an extension of its base type; if base is not specified, the base type is assumed to be anyType. All element groups and attribute definitions must be set inside the <restriction> or <extension> elements.

Important

Whether your new complexType is an extension or a restriction of its base type, the new type must be substitutable for its base type without any problems.

For complex types with simple content, we just replace <complexContent> with <simpleContent>:

<xs:element name="bandMember" type="tns:bandMemberType" /> <xs:complexType name="bandMemberType">   <xs:simpleContent>     <xs:extension base="xs:string">       <xs:attribute name="plays" type="tns:instrument" />     </xs:extension>   </xs:simpleContent> </xs:complexType>

This declaration states that the element <bandMember> will contain a string as text content and have an attribute called plays of type tns:instrument, as in this example:

<x:bandMember plays="guitar voice">Devin Townsend</x:bandMember>

A complex type with mixed content contains both subelements and text; you indicate this type by setting the <complexType> element’s mixed attribute to true, as in this example:

<xs:complexType name="amixedType" mixed="true">   <xs:sequence>     <xs:element name="highlight" type="xs:string" />   </xs:sequence> </xs:complexType>

Complex types are serialized to classes and structures in .NET, so each subelement is translated into a data field or class member. Types containing mixed content are therefore seldom used, if at all, to define messages between services and their clients.

Anonymous Types

The simple and complex types we’ve defined to this point have all been global type declarations to be placed under the root <schema> element and referenced elsewhere by the names we’ve given them. However, it’s entirely feasible to create local types without a name that are specific to a particular element. These types are anonymous and cannot be referenced elsewhere. For example, we can use two anonymous types to define the response from our album entry service:

<xs:element name="submissionReceipt">

The first defines the two elements that a response must contain:

  <xs:complexType>     <xs:sequence>       <xs:element name="message" type="xs:string" />       <xs:element name="albumID">

The second defines a pattern for the value of albumID. In this case, albumID must contain a GUID:

        <xs:simpleType>           <xs:restriction base="xs:string">             <xs:pattern                value="[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-Π  [0-9A-F]{12}"           </xs:restriction>         </xs:simpleType>       </xs:element>     </xs:sequence>   </xs:complexType> </xs:element> 

Element and Attribute Groups

You can use one other shortcut when writing your schema. If you’re using the same set of elements or attributes in your complex types, you can define an element group by using <group> or define an attribute group by using an <attributeGroup> containing those items, and then you just reference the group. For example

<xs:element type="tns:albumType" name="album" /> <xs:complexType name="albumType">   <xs:simpleContent>     <xs:extension base="xs:string">       <xs:attributeGroup ref="tns:albumProperties" />     </xs:extension>   </xs:simpleContent> </xs:complexType> <xs:attributeGroup name="albumProperties">   <xs:attribute name="noOfTracks" type="xs:byte" />   <xs:attribute name="runningTime" type="xs:duration" /> </xs:attributeGroup>

As with elements, attributes, and types, you can define groups with global scope so they can be reused elsewhere by referencing them with a ref instead of a name. Local group definitions remain specific to the definition in which they are created.

Namespace Scoping Issues

Earlier in this chapter, we mentioned a couple of potential namespace scoping issues that you might encounter if you rely on using default namespaces. Here’s an example:

<xs:schema targetNamespace="http://www.notashop.com/wscr"   xmlns:tns="http://www.notashop.com/wscr"   xmlns:xs="http://www.w3.org/2001/XMLSchema">                      <xs:element type="xs:string" name="artist" />   <xs:element name="albumSubmission">     <xs:complexType>       <xs:sequence>         <xs:element ref="tns:artist" />         <xs:element name="album" type="xs:string"/>       </xs:sequence>     </xs:complexType>   </xs:element> </xs:schema>

As you’ll recall, everything declared in a schema is either part of the given targetNamespace or part of no namespace at all. All local declarations, such as that of album, fall into the latter category unless you set their form attribute to qualified or set the appropriate global xxxFormDefault attribute. If you do neither of these, creating instance documents becomes a little tricky.

The following code establishes nicely that the <album> element does not belong to the namespace http://www.notashop.com/wscr:

<x:albumSubmission xmlns:x="http://www.notashop.com/wscr">   <x:artist>Tool</x:artist>   <album>Lateralus</album> </x:albumSubmission>

The distinction is lost if we use a default namespace:

<albumSubmission xmlns="http://www.notashop.com/wscr">     <artist>Tool</artist>     <album>Lateralus</album> </albumSubmission>

This instance document is actually invalid because <album> is not associated with the default namespace—it is has no namespace at all. You can acknowledge this by changing the <album> element to read as follows:

    <album xmlns="">Lateralus</album>

However, the neatest solution is to ensure that instances of local declarations are associated with the target namespace for the schema. That is, we set their form attribute or the appropriate global xxxFormDefault attribute to qualified. With this done in the schema, both examples become valid.

Advanced Schema Features

You’ve seen that defining new types means that the new type inherits the characteristics of the base type and either extends or restricts its definition. The XML Schema allows you to create an even richer inheritance model for instance documents in one of two ways:

  • You can declare an element to be of an abstract base type and thus declare the instance of the element to be of one of the types that derives from the base type.

  • You can specify a substitution group for an abstract type. An element of the base type will then take the form of one of the types in the substitution group.

To complement these polymorphic features, you can restrict which derived types an element can be assigned. You do this by specifying how the base type can be derived, if at all, and by blocking certain types of derivation on a per-element basis.

Unfortunately, because of a bug, the XML serializer library doesn’t correctly form instance documents against schemas that use these features. Therefore, we’ll skip the actual details for now. If you’d like to learn more about these schema features, check out Aaron Skonnard’s article “Understanding XML Schema” at http://msdn.microsoft.com/webservices.




Programming Microsoft. NET XML Web Services
Programming MicrosoftВ® .NET XML Web Services (Pro-Developer)
ISBN: 0735619123
EAN: 2147483647
Year: 2005
Pages: 172

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