Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Simplifying Serialization and De-serialization Processes With XSD.EXE

30 Jan 2006 1  
XSD.EXE is an XML Schema Definition tool that generates XML schema or common language runtime classes from XDR, XML, and XSD files, or from classes in a runtime assembly.

Introduction

XSD.EXE is an XML Schema Definition tool that generates XML schema or common language runtime classes from XDR, XML, and XSD files, or from classes in a runtime assembly.

An XML-schema is a document that describes the valid format of an XML data-set. This definition includes what elements are (and are not) allowed at any point, what the attributes for any element may be, the number of occurrences of elements, etc.

Deserialization Process

Let us assume you are building a web service, which takes XML (in the form of string) as input, and shown below is the schema of the data that is to be exchanged between the clients and your web service.

<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:complexType name="EmployeeType">
       <xsd:sequence>
        <xsd:element name="EmployeerID" type="xsd:int" />
        <xsd:element name="Name" type="NameType" />
        <xsd:element name="SSN" 
                   type="xsd:string" minOccurs="0" />
        <xsd:element name="CurrentAddress" 
                   type="AddressType" />
        <xsd:element name="PreviousAddress" 
                   type="AddressType" 
                   minOccurs="0" maxOccurs="2" />
        <xsd:element name="DriverLicense" 
                   type="xsd:string" minOccurs="0" />
        <xsd:element name="Phone" 
                   type="PhoneType" 
                   minOccurs="0" maxOccurs="3" />
        <xsd:element name="DOB" 
                   type="xsd:string" minOccurs="0" />
        <xsd:element name="YOB" 
                   type="xsd:string" minOccurs="0" />
       </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="NameType">
        <xsd:annotation>
            <xsd:documentation />
        </xsd:annotation>
        <xsd:sequence>
            <xsd:element name="Surname" 
                   type="xsd:string"></xsd:element>
            <xsd:element name="First" 
                   type="xsd:string"></xsd:element>
            <xsd:element name="Middle" minOccurs="0" 
                   type="xsd:string"></xsd:element>
            <xsd:element name="Gen" minOccurs="0" 
                   type="xsd:string"></xsd:element>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="AddressType">
        <xsd:sequence>
            <xsd:element name="StreetNumber" minOccurs="0" 
                   type="xsd:int"></xsd:element>
            <xsd:element name="StreetName" 
                   type="xsd:string"></xsd:element>
            <xsd:element name="City" 
                   type="xsd:string"></xsd:element>
            <xsd:element name="State" 
                   type="xsd:string"></xsd:element>
            <xsd:element name="Zip" 
                   type="xsd:string"></xsd:element>
        </xsd:sequence>
    </xsd:complexType>
    <xsd:complexType name="PhoneType">
        <xsd:sequence>
            <xsd:element name="Type" minOccurs="0" 
                   type="xsd:string"></xsd:element>
            <xsd:element name="Number" type="xsd:string" />
        </xsd:sequence>
    </xsd:complexType>
    <xsd:element name="EmpType" 
                   type="EmployeeType"></xsd:element>
</xsd:schema>

To deserialize, first we need to build a class as below:

[System.Xml.Serialization.XmlRootAttribute("EmpType", 
            Namespace="", IsNullable=false)]
public class EmployeeType 
{
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public int EmployeerID;

  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public NameType Name;
    . . . . . .
    . . . . . .
    . . . . . .
}

public class NameType 
{
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string Surname;
     . . . . . .
     . . . . . .
     . . . . . .
}
public class PhoneType 
{
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string Type;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string Number;
}

public class AddressType 
{
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public int StreetNumber;
    . . . . . .
    . . . . . .
    . . . . . .
}

Building such a class by seeing the XML schema is a time consuming and error prone process. An alternate approach is to use XSD.EXE.

First, build the class using the XSD.EXE tool provided by the .NET framework.

///////////Syntax of xsd.exe/////////

XSD.EXE file.xsd /classes /language:[language]

The output class is as follows:

using System.Xml.Serialization;

[System.Xml.Serialization.XmlRootAttribute(
  "EmpType", Namespace="", IsNullable=false)]
public class EmployeeType
{
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public int EmployeerID;

  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public NameType Name;

  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string SSN;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public AddressType CurrentAddress;

  [System.Xml.Serialization.XmlElementAttribute("PreviousAddress",
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public AddressType[] PreviousAddress;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string DriverLicense;

  [System.Xml.Serialization.XmlElementAttribute("Phone",
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public PhoneType[] Phone;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string DOB;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string YOB;
}

public class NameType
{
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string Surname;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string First;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string Middle;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string Gen;
}

public class PhoneType
{
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string Type;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string Number;
}

public class AddressType
{
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public int StreetNumber;

  [System.Xml.Serialization.XmlIgnoreAttribute()]
  public bool StreetNumberSpecified;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string StreetName;
  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string City;

  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string State;

  [System.Xml.Serialization.XmlElementAttribute(
    Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
  public string Zip;
}

If you see the file generated by the XSD.EXE tool, it has added a boolean variable StreetNumberSpecified, which indicates whether the StreetNumber element was supplied in the input or not. So whenever we are about to use the StreetNumber, first we need to check StreetNumberSpecified, and if it is true then we can use the StreetNumber variable. Similarly, it has added a boolean variables for other elements whose minOccurs="0� and whose data type is int. If the data type is string then it will not add the boolean variable that corresponds to the string variable.

Now, add this class to your web service. Then, change the deserialization process as follows:

public string ProcessRequest (string strEMPDet)
{
    try
    {
        string strEMPDet = 
          ConfigurationSettings.AppSettings.Get("XSDPath");
        XmlValidatingReader vr = new 
          XmlValidatingReader(strEMPDet, XmlNodeType.Element);
        vr.ValidationType = ValidationType.Schema;
        vr.Schemas.Add( ��, strXSDFilePath );
        while( vr.Read() )
        {
        }
    }
    catch( XMLException ex )
    {
        //log exception

    }
}

The validating reader class is inherited from an XML reader. The validating reader examines and validates each single piece of XML according to the requested validation type. The XmlValidatingReader class does not explicitly provide a single method good at validating the whole content of a document, instead it validates reader works incrementally, node by node, as the underlying reader proceeds. If just write a try catch block we can�t track all the validation errors, i.e., we can't trap all the nodes that are not following the schema. In order to track messages and detects errors (all nodes), the application must define an event handler. If a validating reader happens to work on a badly-formed XML document, no event is fired but an XmlException exception is raised.

The handler for the event has the following signature:

public delegate void ValidationEventHandler(object sender, 
                     ValidationEventArgs e);

The complete code is:

StringBuilder alValidationErrors =new StringBuilder();
[WebMethod]
public bool ProcessRequest(string strEMPDet)
{
    EmployeeType objEmp=new EmployeeType();
    try
    {
        string strXSDFile = 
          ConfigurationSettings.AppSettings.Get("XSDPath" );;
        string strRequestXSDNameSpace="";
        //Read in the input XML in a TextReader...

        TextReader tr = (TextReader)new StringReader(strEMPDet);
    
        //Create a validating XML reader and validate...

        XmlParserContext context = new XmlParserContext(null, 
                                   null, "", XmlSpace.None );
        XmlValidatingReader vr = new 
          XmlValidatingReader(strEMPDet, 
          XmlNodeType.Element, context );
        vr.ValidationType = ValidationType.Schema;
        vr.Schemas.Add( strRequestXSDNameSpace, strXSDFile );
        vr.ValidationEventHandler += new 
          ValidationEventHandler(this.ValidationEventHandle );
        while( vr.Read() ){}
        //Now deserialize.

        objEmp = ( EmployeeType ) ( new XmlSerializer( 
                   typeof( EmployeeType ) ) ).Deserialize( tr );
    }
    catch( Exception ex)
    {
        //logging

        return false;
    }
    return true;
}

private void ValidationEventHandle (object sender, ValidationEventArgs args)
{
    alValidationErrors.Append(args.Message + Environment.NewLine);
}

With this approach, we can find out the list of all nodes that are not following the XSD.

The advantages of this approach are:

  1. Building the class to hold the incoming data is easy.
  2. Whenever there is a change in schema, we need to update the file.xsd alone, no need to change the deserialization process and rebuild the application.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here