XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

The .NET Schema Object Model
by Priya Lakshminarayanan | Pages: 1, 2, 3

Traversal

Depending on what we need to manipulate in the schema, we have to traverse one or more of these collections. The following is a small code sample to traverse the Customer schema and print out the global elements and their attributes:

private static XmlSchema 
ReadAndCompileSchema(string fileName) {
    XmlTextReader tr = new XmlTextReader(fileName, 
        new NameTable());

    // The Read method will throw errors encountered
    // on parsing the schema
    XmlSchema schema = XmlSchema.Read(tr, 
        new ValidationEventHandler(ValidationCallbackOne));
    tr.Close();

    // The Compile method will throw errors 
    // encountered on compiling the schema
    schema.Compile(new ValidationEventHandler
                    (ValidationCallbackOne));
    return schema;
}

public static void ValidationCallbackOne(object sender, 
    ValidationEventArgs args) {
    Console.WriteLine(“Exception Severity: “ + args.Severity);
    Console.WriteLine(args.Message);
}

private void TraverseSOM() {
    XmlSchema custSchema = ReadAndCompileSchema(
                              "Customer.xsd");

    foreach (XmlSchemaElement elem in 
                 custSchema.Elements.Values) {

        Console.WriteLine("Element: {0}", elem.Name);

        if (elem.ElementType is XmlSchemaComplexType) {

            XmlSchemaComplexType ct = 
                (XmlSchemaComplexType) elem.ElementType;

            if (ct.AttributeUses.Count > 0) {

                IDictionaryEnumerator ienum = 
                    ct.AttributeUses.GetEnumerator();

                while (ienum.MoveNext()) {

                    XmlSchemaAttribute att = 
                       (XmlSchemaAttribute) ienum.Value;

                    Console.Write("Attribute: {0}  ",
                                  att.Name);
                }
            }
        }
    }
}

The ElementType can be XmlSchemaType (XmlSchemaSimpleType or XmlSchemaComplexType) if it is a user-defined simple type or complex type; or it can be XmlSchemaDatatype if it is one of the built-in datatypes defined in XML Schema Part 2: Datatypes.

For our Customer schema, the ElementType of the Customer element is XmlSchemaComplexType, FirstName element is XmlSchemaDatatype, and LastName element is XmlSchemaSimpleType. The first code sample to create a Customer schema made use of the Attributes collection in the XmlSchemaComplexType class to add the attribute CustID to the Customer element. This is a pre-schema-compilation property. The corresponding post-schema-compilation property is the AttributeUses collection on the XmlSchemaComplexType class, which holds all the attributes of the complex type, including the ones that are inherited through type derivation. It's more useful to query this property once the schema has been compiled.

Editing

Editing is one of the most important usage scenarios for the SOM. All the pre-schema-compilation properties can be set to change the existing values, and the schema can be re-compiled to reflect changes in the object model. The following two samples illustrate editing scenarios in the SOM.

Sample 1

Let's modify our Customer schema by adding a new element "PhoneNumber" to the Customer element. We load the schema into the SOM using the Read method, traverse to the element, retrieve its complexType and add an element to it. Since the schema has been already created and compiled, we can iterate through its "Elements" post-schema-compilation property to find the "Customer" element.

The ElementType is also part of the PSCI: it is the compiled complex type of the "customer" element. Since we need to add another element to the "Customer" element, we can retrieve the particle of the complex type and add our new "PhoneNumber" element to it, which the following code accomplishes:

private void ModifySOM1() {

    XmlSchema custSchema = ReadAndCompileSchema("Customer.xsd");

    // Create the new element to be added
    XmlSchemaElement phoneElem = new XmlSchemaElement();
    phoneElem.Name = "PhoneNumber";

    // Create a simple type with a pattern for the 
    // phone number element
    XmlSchemaSimpleType phoneType = new XmlSchemaSimpleType();
    XmlSchemaSimpleTypeRestriction restr = new 
        XmlSchemaSimpleTypeRestriction();
    restr.BaseTypeName = new XmlQualifiedName("string",
                             "http://www.w3.org/2001/XMLSchema");

    // Create a Pattern facet
    XmlSchemaPatternFacet phPattern = new XmlSchemaPatternFacet();
    // The value for the pattern is a Regular expression that allows
    // 3 digits followed by "-", then 3 digits, another "-" and 4
    // more digits
    phPattern.Value = "\\d{3}-\\d{3}-\\d{4}";
    restr.Facets.Add(phPattern);

    // Set the content of the simple type to be the simpleType
    // restriction created above
    phoneType.Content = rest;

    // Set the schema type of the PhoneNumber element to be
    //  the anonymous simple type
    phoneElem.SchemaType = phoneType;

    // Locate position to add the PhoneNumber element
    foreach(XmlSchemaElement elem in custSchema.Elements.Values) {
        if(elem.QualifiedName.Name.Equals("Customer")) {
            //get the Complex type of the element
            XmlSchemaComplexType custType = 
                (XmlSchemaComplexType) elem.ElementType;
            XmlSchemaSequence seq = 
                (XmlSchemaSequence) custType.Particle;
            seq.Items.Add (phoneElem);
            break;
        }
    }

    // Recompile schema
    custSchema.Compile(new 
        ValidationEventHandler(ValidationCallbackOne));
    WriteCustomerSchema(custSchema);
}

And emits the following (reformatted for readability.)

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://tempuri.org" 
           targetNamespace="http://tempuri.org" 
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Customer">
    <xs:complexType>
       <xs:sequence>
         <xs:element name="FirstName" type="xs:string" />
         <xs:element name="LastName" type="tns:LastNameType" />
         <xs:element name="PhoneNumber">
           <xs:simpleType>
            <xs:restriction base="xs:string">
            <xs:pattern value="\d{3}-\d{3}-\d{4}" />
           </xs:restriction>
           </xs:simpleType>
         </xs:element>
       </xs:sequence>
       <xs:attribute name="CustID" type="xs:positiveInteger"
                     use="required" />
    </xs:complexType>
   </xs:element>
   <xs:simpleType name="LastNameType">
     <xs:restriction base="xs:string">
       <xs:maxLength value="20"/>
     </xs:restriction>
   </xs:simpleType>
</xs:schema>

Sample 2

In this sample we add an attribute Title to the FirstName element. The FirstName element's type is "xs:string". For it to have an attribute along with string content, we need to change its type to be a complex type, with simple content extension content model.

Since the FirstName element is not a global element in the schema, but a child element of Customer, it is not directly available in the Items or Elements collections. We need to locate the Customer element first before we can edit the FirstName element.

In Sample 1 we saw how to iterate through the XmlSchema Elements PSCI property. In this sample, we iterate through the collection returned by the Items property. Though both properties provide access to the global elements, iterating through the Items collection is more time-consuming since we have to go over all the global components in the schema, and it does not have any post-compilation properties available. By contrast, the PSCI collections (Elements, Attributes, SchemaTypes, etc.) listed in Table 2 provide direct access to the corresponding global element, attribute, or type and their PSCI properties.

private static void ModifySOM2() {
    // Read the customer schema into the SOM
    XmlSchema custSchema = ReadAndCompileSchema("Customer.xsd");

    // Create Complex type for FirstName Element
    XmlSchemaComplexType ct = new XmlSchemaComplexType();
    ct.Name = "FirstNameCT";
    XmlSchemaSimpleContent sct = new XmlSchemaSimpleContent();

    // Create simple content extension
    XmlSchemaSimpleContentExtension simpleExt =
        new XmlSchemaSimpleContentExtension();
    simpleExt.BaseTypeName = new XmlQualifiedName("string",
        "http://www.w3.org/2001/XMLSchema");

    // Create attribute to be added
    XmlSchemaAttribute titleAtt = new XmlSchemaAttribute();
    titleAtt.Name = "Title";
    titleAtt.SchemaTypeName = new XmlQualifiedName("string",
       "http://www.w3.org/2001/XMLSchema");

    // Add attribute to simple content extension
    simpleExt.Attributes.Add(titleAtt);
    
    // Set content of the simple content and content 
    // model of complex type
    sct.Content = simpleExt;
    ct.ContentModel = sct;

    // Add the new complex type to the XmlSchema Items collection
    custSchema.Items.Add(ct);

    foreach (XmlSchemaObject xso in custSchema.Items) {
       if (xso is XmlSchemaElement) {
           XmlSchemaElement elem = (XmlSchemaElement)xso;
           if (elem.QualifiedName.Name.Equals("Customer")) {
               XmlSchemaComplexType custType = 
                   (XmlSchemaComplexType)elem.ElementType;
               XmlSchemaSequence children = 
                   (XmlSchemaSequence)custType.Particle;
               foreach(XmlSchemaParticle p1 in children.Items) {
                   // Check if it is element
                   if (p1 is XmlSchemaElement) {
                       XmlSchemaElement childElem = 
                          (XmlSchemaElement)p1;

                       if (childElem.Name.Equals("FirstName")) {
                           childElem.SchemaTypeName = 
                               new XmlQualifiedName("FirstNameCT", 
                                 "http://tempuri.org");
                           break;
                       }
                   }
               }
           }
       }
    }
    custSchema.Compile(new ValidationEventHandler(ValidationCallbackOne));
    WriteCustomerSchema(custSchema);
}

The following output is generated by the above sample:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:tns="http://tempuri.org" 
            targetNamespace="http://tempuri.org"
            xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Customer">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="FirstName" type="tns:FirstNameCT" />
        <xs:element name="LastName" type="tns:LastNameType" />
        <xs:element name="PhoneNumber">
          <xs:simpleType>
            <xs:restriction base="xs:string">
              <xs:pattern value="\d{3}-\d{3}-\d{4}" />
            </xs:restriction>
          </xs:simpleType>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="CustID" type="xs:positiveInteger" use="required" />
    </xs:complexType>
  </xs:element>
  <xs:simpleType name="LastNameType">
    <xs:restriction base="xs:string">
      <xs:maxLength value="20"/>
    </xs:restriction>
  </xs:simpleType>
  <xs:complexType name="FirstNameCT">
    <xs:simpleContent>
      <xs:extension base="xs:string">
        <xs:attribute name="Title" type="xs:string" />
      </xs:extension>
    </xs:simpleContent>
 </xs:complexType>
</xs:schema>


Pages: 1, 2, 3

Next Pagearrow