[ Team LiB ] Previous Section Next Section

8.3 Working with Schemas

Because the XML Schema language is expressed as an XML vocabulary, it can be dealt with just as any other XML document. XmlReader, XmlWriter, XmlDocument, XPathNavigator, XsltTransform, and the rest of .NET's types can be used to read, write, manipulate, navigate, and transform an XSD document.

In addition to the standard ways of dealing with XML documents, however, the System.Xml.XmlSchema assembly includes a host of types that can be used to deal with schema documents in a very XSD-centric way.

8.3.1 Creating a Schema Programmatically

As you already know, xs:schema is the root element of an XSD document. XmlSchema is the type that represents the xs:schema element.

XmlSchema is a subclass of XmlSchemaObject, whose other subclasses are XmlSchemaAnnotated, XmlSchemaAnnotation, XmlSchemaAppInfo, XmlSchemaDocumentation, and XmlSchemaExternal. Each of these subclasses represents a specific type of XML Schema element, and some of them have their own subclasses. The .NET XmlSchema type hierarchy is shown in Figure 8-1.

Figure 8-1. XmlSchema type hierarchy
figs/dnxm_0801.gif

Table 8-4 shows each XML Schema element name with its corresponding .NET type. In some cases, more than one .NET class is used for the same XML Schema element; typically, this is the case when the same element has different behavior in different contexts. There are many more types in the System.Xml.Schema assembly that do not correspond directly to an XML Schema element, and they are listed in the assembly reference in Chapter 17.

Table 8-4. XML Schema element names and .NET types

XML Schema element name

.NET type

xs:all

XmlSchemaAll

xs:annotation

XmlSchemaAnnotation

xs:any

XmlSchemaAny

xs:anyAttribute

XmlSchemaAnyAttribute

xs:appinfo

XmlSchemaAppInfo

xs:attribute

XmlSchemaAttribute

xs:attributeGroup

XmlSchemaAttributeGroup

XmlSchemaAttributeGroupRef

xs:choice

XmlSchemaChoice

xs:complexContent

XmlSchemaComplexContent

xs:complexType

XmlSchemaComplexType

xs:documentation

XmlSchemaDocumentation

xs:element

XmlSchemaElement

xs:enumeration

XmlSchemaEnumerationFacet

xs:extension

XmlSchemaComplexContentExtension

XmlSchemaSimpleContentExtension

xs:field

Collection of XmlSchemaXPath

xs:fractionDigits

XmlSchemaFractionDigitsFacet

xs:group

XmlSchemaGroup

XmlSchemaGroupRef

xs:import

XmlSchemaImport

xs:include

XmlSchemaInclude

xs:key

XmlSchemaKey

xs:keyref

XmlSchemaKeyRef

xs:length

XmlSchemaLengthFacet

xs:list

XmlSchemaSimpleTypeList

xs:maxExclusive

XmlSchemaMaxExclusiveFacet

xs:maxInclusive

XmlSchemaMaxInclusiveFacet

xs:maxLength

XmlSchemaMaxLengthFacet

xs:minExclusive

XmlSchemaMinExclusiveFacet

xs:minInclusive

XmlSchemaMinInclusiveFacet

xs:minLength

XmlSchemaMinLengthFacet

xs:notation

XmlSchemaNotation

xs:pattern

XmlSchemaPatternFacet

xs:redefine

XmlSchemaRedefine

xs:restriction

XmlSchemaComplexContentRestriction

XmlSchemaSimpleContentRestriction

XmlSchemaSimpleTypeRestriction

xs:schema

XmlSchema

xs:selector

XmlSchemaXPath

xs:sequence

XmlSchemaSequence

xs:simpleContent

XmlSchemaSimpleContent

xs:simpleType

XmlSchemaSimpleType

xs:totalDigits

XmlSchemaTotalDigitsFacet

xs:union

XmlSchemaSimpleTypeUnion

xs:unique

XmlSchemaUnique

xs:whiteSpace

XmlSchemaWhitespaceFacet

You can access all the attributes of each XSD element directly through the corresponding .NET type's properties; each property is named the same as the corresponding attribute, with an initial capital letter. In those cases where an attribute is not valid because a particular element can appear in different contexts, an exception is thrown if you try to access the corresponding property.

Creating an instance of an XSD programmatically, then, is a fairly simple process, illustrated in Example 8-7. The overall process should be reminiscent of creating a DOM instance with XmlDocument.

Example 8-7. Creating an XSD instance programmatically
using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;

public class CreateSchema {
  public static void Main(string [ ] args) {
    string ns = "http://www.w3.org/2001/XMLSchema";
    XmlQualifiedName idType = new XmlQualifiedName("ID",ns);
    XmlQualifiedName stringType = new XmlQualifiedName("string",ns);
    XmlQualifiedName tokenType = new XmlQualifiedName("token",ns);

    XmlSchema schema = new XmlSchema( );
    schema.Version = "1.0";

    XmlSchemaElement customer = new XmlSchemaElement( );
    customer.Name = "Customer";
    schema.Items.Add(customer);

    XmlSchemaComplexType customerComplexType = new XmlSchemaComplexType( );
    customer.SchemaType = customerComplexType;

    XmlSchemaSequence customerSequence = new XmlSchemaSequence( );
    customerComplexType.Particle = customerSequence;

    XmlSchemaElement name = new XmlSchemaElement( );
    name.Name = "Name";
    name.MinOccurs = 1;
    name.MaxOccurs = 1;
    name.SchemaTypeName = tokenType;
    customerSequence.Items.Add(name);

    XmlSchemaElement address = new XmlSchemaElement( );
    address.Name = "Address";
    address.MinOccurs = 1;
    address.MaxOccursString = "unbounded";
    customerSequence.Items.Add(address);

    XmlSchemaComplexType addressComplexType = new XmlSchemaComplexType( );
    address.SchemaType = addressComplexType;

    XmlSchemaSequence addressSequence = new XmlSchemaSequence( );
    addressComplexType.Particle = addressSequence;

    XmlSchemaElement street = new XmlSchemaElement( );
    street.Name = "Street";
    street.MinOccurs = 1;
    street.MaxOccurs = 3;
    street.SchemaTypeName = stringType;
    addressSequence.Items.Add(street);

    XmlSchemaElement city = new XmlSchemaElement( );
    city.Name = "City";
    city.MinOccurs = 1;
    city.MaxOccurs = 1;
    city.SchemaTypeName = stringType;
    addressSequence.Items.Add(city);

    XmlSchemaElement state = new XmlSchemaElement( );
    state.Name = "State";
    state.MinOccurs = 1;
    state.MaxOccurs = 1;
    state.SchemaTypeName = stringType;
    addressSequence.Items.Add(state);

    XmlSchemaElement zip = new XmlSchemaElement( );
    zip.Name = "Zip";
    zip.MinOccurs = 1;
    zip.MaxOccurs = 1;
    zip.SchemaTypeName = new XmlQualifiedName("USZipCodeType");
    addressSequence.Items.Add(zip);

    XmlSchemaAttribute customerId = new XmlSchemaAttribute( );
    customerId.Name = "Id";
    customerId.SchemaTypeName = idType;
    customerComplexType.Attributes.Add(customerId);

    XmlSchemaSimpleType usZipCodeType = new XmlSchemaSimpleType( );
    usZipCodeType.Name = "USZipCodeType";
    schema.Items.Add(usZipCodeType);

    XmlSchemaSimpleTypeRestriction zipRestriction = new XmlSchemaSimpleTypeRestriction( );
    zipRestriction.BaseTypeName = tokenType;
    usZipCodeType.Content = zipRestriction;

    XmlSchemaPatternFacet zipPattern = new XmlSchemaPatternFacet( );
    zipPattern.Value = @"\d{5}(-\d{4})?";
    zipRestriction.Facets.Add(zipPattern);

    schema.Compile(new ValidationEventHandler(Handler));

    if (schema.IsCompiled) {
      Console.WriteLine("Schema compiled.");
      schema.Write(File.Create("Customer.xsd"));
    } else {
      Console.WriteLine("Schema compilation failed.");
    }
  }

  public static void Handler(object sender, ValidationEventArgs e) {
    Console.WriteLine(e.Message);
  }
}

Running this code produces an XSD equivalent to the Customer.xsd that started the chapter (there are minor differences, such as the order of attributes, but the canonical XML is the same). Most of this code should be self-explanatory. The general process takes the following steps:

  1. Create an instance of an XmlSchemaObject subclass.

  2. Set the object's properties.

  3. Add the object to its parent's list of children.

A few particular sections of code do bear further explanation:

XmlQualifiedName idType = new XmlQualifiedName("ID",ns);
XmlQualifiedName stringType = new XmlQualifiedName("string",ns);
XmlQualifiedName tokenType = new XmlQualifiedName("token",ns);

These lines create instances of the type XmlQualifiedName. XmlQualifedName represents a qualified name in XML, in the format namespace:localname. The document's XmlNameTable is used to resolved the namespace URI in the second parameter to the appropriate prefix; in this case, that's xs. You'll use these XmlQualifiedName instances later, when setting the type attributes of xs:element and xs:attribute elements:

zip.SchemaTypeName = new XmlQualifiedName("USZipCodeType");

This line sets the Zip element's type attribute to USZipCodeType. This simple type hasn't actually been defined yet at this point, but that's ok. The document need not be valid until it is compiled; in fact, an invalid XSD may be written to disk without any error being raised. That's why the last few lines of the program are included:

if (schema.IsCompiled) {
  Console.WriteLine("Schema compiled.");
  schema.Write(File.Create("Customer.xsd"));
} else {
  Console.WriteLine("Schema compilation failed.");
}

These lines make sure that the XSD is valid before writing it to disk. Although the ValidationEventHandler would have written a message to the console if there were any problems, the IsCompiled property of the XmlSchema instance can be queried as a final check before writing the XSD to disk.

The entire process is fairly straightforward, and should need no further explanation.

8.3.2 Manipulating an Existing Schema

You already know how to load any XML document into memory. Once you have a document in memory, you can navigate through its elements using XmlDocument's standard methods, and read it into an XmlSchema for other purposes:

XmlDocument document = new XmlDocument( );
document.Load(args[0]);

XmlNodeReader reader = new XmlNodeReader(document);

ValidationEventHandler handler = new ValidationEventHandler(Handler);
XmlSchema schema = XmlSchema.Read(reader, handler);

schema.Compile(handler);

In much the same way, you can use XmlDocument's GetNavigator( ), SelectNodes( ), and SelectSingleNode( ) methods to navigate an XSD. You can also transform it into any other format, given an appropriate XSLT stylesheet. For example, you might wish to transform an XSD into a DTD; you could write an XSLT transform to do so.

    [ Team LiB ] Previous Section Next Section