Generating C# Code from XSD

Previous Menu Next

Generating C# Code from XML Schemas

Let's look again at our Purchase.xsd file from previously, which is reproduced here:

   <xsd:element name="purchase" type="PurchaseRecord"/>

   <xsd:complexType name="PurchaseRecord">
      <xsd:sequence>
         <xsd:element name="customer" type="CustomerType" maxOccurs="1"/>
         <xsd:element name="store" type="xsd:string" maxOccurs="1"/>
         <xsd:sequence maxOccurs="unbounded">
            <xsd:element name="item" type="xsd:string"/>
            <xsd:element name="price" type="xsd:float"/>
         </xsd:sequence>
      </xsd:sequence>
   </xsd:complexType>

   <xsd:complexType name="CustomerType">
      <xsd:simpleContent>
         <xsd:extension base="xsd:string">
            <xsd:attribute name="number" type="xsd:integer"/>
         </xsd:extensiion>
      </xsd:simpleContent>
   </xsd:complexType>
   

To generate C# code from this schema, we could use the following XBinder command:

        
           > xbinder Purchase.xsd -csharp -xml -csnsname sample.purchase
        
       

The -csnsname command-line option is not required, but it can be useful for controlling the name of the C# namespace with which the generated classes are associated. Without it, the namespace name would default to the name of the current directory. So, for example, if your current directory is "mydocs", the namespace for the generated code will also be called "mydocs".

This command will produce five files, as follows:

FilePurpose
_Purchase.csContains some miscellaneous definitions that may be used by other generated code.
CustomerType.cs Contains the definitions for the CustomerType class; i.e., the class that corresponds to the complex type that defines the <customer> element within a PurchaseRecord.
Purchase_CC.cs Contains the definitions for the Purchase control class; i.e., the class that corresponds to the <purchase> global element that's declared at the top of the .xsd file (though it doesn't have to be) and that encapsulates everything else.
PurchaseRecord.cs Contains the definitions for the PurchaseRecord class; i.e., the class that corresponds to the complex type that defines the global <purchase> element.
PurchaseRecord_3.cs Contains the definitions to encapsulate and manage the items that might repeat within a PurchaseRecord. The number 3 is used simply because the sequence defining these potentially repeating items is the third item in the PurchaseRecord.

Let's look a little more closely at some of the classes generated for the element declarations in the .xsd file. We will look first at the code that gets generated for the CustomerType. Here is the schema declaration again:

      <xsd:complexType name="CustomerType">
      <xsd:simpleContent>
         <xsd:extension base="xsd:string">
            <xsd:attribute name="number" type="xsd:integer"/>
         </xsd:extensiion>
      </xsd:simpleContent>
   </xsd:complexType>
   

The defining code that gets generated in CustomerType.cs looks like this:

   public class CustomerType : com.objsys.xbinder.runtime.XBComplexType
   {

      public static readonly XBQualifiedName XSD_TYPE = new XBQualifiedName("http://www.example.org/Purchase",
         "CustomerType");

      //attribute fields
      protected int number;
      protected bool _set_number = false;

      //Empty element indicator.  This variable may stay false
      //even if an element is empty because emptiness is
      //sometimes detected by another means.
      protected bool elementEmpty = false;

      //content fields
      protected string value;

      //attribute methods

      public int getNumber() {
         if (!_set_number)throw new XBException("field number not set");
         return this.number;
      }

      public bool isSetNumber() {
         return _set_number;
      }

      public void setNumber(int value) {
         this.number = value;
         _set_number = true;
      }

      public void setNumber() {
         _set_number = false;
      }

      //content methods

      public string getValue() {
         return this.value;
      }

      public void setValue(string value) {
         this.value = value;
      }

      // Other methods to manage the instance, such as encoding and
      // decoding methods, follow after this point, but are not reproduced.
   }

Take note of the following:

  • The complexType definition in the .xsd file results in a class definition in the .cs file.
  • Attributes that may appear in an element instance each have a boolean defined in the class to indicate whether they're present.
  • The base value of the element is represented as a string member of the class.
  • The value of the attribute is represented as an integer member of the class.
  • The generated class extends a class called XBComplexType in a namespace named com.objsys.xbinder.runtime. This namespace and class are part of the XBinder C# runtime components, which come with the product.

Now let's look at the code that gets generated for the PurchaseRecord, which appears like this in the schema:

   <xsd:complexType name="PurchaseRecord">
      <xsd:sequence>
         <xsd:element name="customer" type="CustomerType" maxOccurs="1"/>
         <xsd:element name="store" type="xsd:string" maxOccurs="1"/>
         <xsd:sequence maxOccurs="unbounded">
            <xsd:element name="item" type="xsd:string"/>
            <xsd:element name="price" type="xsd:float"/>
         </xsd:sequence>
      </xsd:sequence>
   </xsd:complexType>

The defining code that gets generated in PurchaseRecord.cs looks like this:

   public class PurchaseRecord : com.objsys.xbinder.runtime.XBComplexType
   {

      public static readonly XBQualifiedName XSD_TYPE = 
         new XBQualifiedName("http://www.example.org/Purchase", "PurchaseRecord");

      //particleInfo[i] provides information for the ith particle.
      //For elements, it is a XBQualifiedName, identifying the expected element.
      //For element wildcards, it is a String[], listing the allowed namespaces
      protected static Object[] particleInfo = {
         new XBQualifiedName("http://www.example.org/Purchase","customer"),
         new XBQualifiedName("http://www.example.org/Purchase","store")
      };

      //attribute fields

      //Empty element indicator.  This variable may stay false
      //even if an element is empty because emptiness is
      //sometimes detected by another means.
      protected bool elementEmpty = false;

      //content fields
      protected sample.purchase.CustomerType customer;
      protected string store;
      protected System.Collections.Generic.IList<sample.purchase.PurchaseRecord_3> purchaseRecord_3;


      //attribute methods

      //content methods

      public sample.purchase.CustomerType getCustomer() {
         if (this.customer == null)
            throw new XBException("field customer not set");
         return this.customer;
      }

      public bool isSetCustomer() {
         return this.customer != null;
      }

      public void setCustomer(sample.purchase.CustomerType value) {
         if ( value == null )  {
            this.customer = null;
         }
         else  {
            this.customer = value;
         }
      }

      public string getStore() {
         return this.store;
      }

      public void setStore(string value) {
         this.store = value;
      }

      public System.Collections.Generic.IList<sample.purchase.PurchaseRecord_3> getPurchaseRecord_3()
      {
         if (this.purchaseRecord_3 == null) {
            this.purchaseRecord_3 = new List<sample.purchase.PurchaseRecord_3>();
         }
         return this.purchaseRecord_3;
      }

      public bool isSetPurchaseRecord_3() {
         return this.purchaseRecord_3 != null;
      }

      public void unsetPurchaseRecord_3() {
         this.purchaseRecord_3 = null;
      }

      // Other methods to manage the instance, such as encoding and
      // decoding methods, follow after this point, but are not reproduced.
   }

Take note of the following:

  • The customer element in the purchase record is simply a member of type CustomerType, the definition for which we just examined.
  • The store element is simply a string member of the class.
  • The collection of items and prices, which might repeat within an instance, is represented by a list of instances of another class called PurchaseRecord_3. Let's look now at the definition of PurchaseRecord_3.

XBinder generates a separate class for the elements within the PurchaseRecord that might repeat. This class, called PurchaseRecord_3 (3 simply because it's the third element in the PurchaseRecord) is defined as follows in PurchaseRecord_3.cs:

   public class PurchaseRecord_3
   {

      public static readonly XBQualifiedName XSD_TYPE = new XBQualifiedName("http://www.example.org/Purchase",
         "PurchaseRecord_3");

      //particleInfo[i] provides information for the ith particle.
      //For elements, it is a XBQualifiedName, identifying the expected element.
      //For element wildcards, it is a String[], listing the allowed namespaces
      protected static Object[] particleInfo = {
         new XBQualifiedName("http://www.example.org/Purchase","item"),
         new XBQualifiedName("http://www.example.org/Purchase","price")
      };

      public static bool acceptsElem(String elemNs, String elemLocalName) {
         if (XBQualifiedName.namesMatch(
            (XBQualifiedName)particleInfo[0], elemNs, elemLocalName)) return true;
         return false;
      }

      //Empty element indicator.  This variable may stay false
      //even if an element is empty because emptiness is
      //sometimes detected by another means.
      protected bool elementEmpty = false;

      //content fields
      protected string item;
      protected float price;

      //content methods

      public string getItem() {
         return this.item;
      }

      public void setItem(string value) {
         this.item = value;
      }

      public float getPrice() {
         return this.price;
      }

      public void setPrice(float value) {
         this.price = value;
      }

      // Other methods to manage the instance, such as encoding and
      // decoding methods, follow after this point, but are not reproduced.
   }

Here we see that the item element is simply expressed as a string class member, and the price element is a float class member.

Previous Menu Next