Wednesday, April 6, 2011

Validating XML Files Against XSD Schemas in .NET 1.0, 2.0 and 3.5

Abstract
Validating XML files is a common task in software development for developers. There are three ways to validate XML files: XSD Schema, DTD and Relax NG. In .NET XSD Schema is the preferred way to validate XML files. In this article I will dicuss both approaches and will give code samples to implement them both.


Introduction

XSD Schema, DTD and Relax NG are three common ways to validate a XML file structure.  The first approach, XSD Schema, has been supported by Microsoft .NET Framework and is the best approach for .NET developers.  Schema files are a special kind of XML file that define the structure of an XML file via some pre-defined elements and attributes.
Using this Schema file you can validate your XML file to make sure it fits to your application's needs to prevent any exception in your code.  There are several free or commercial tools which do this validation for you, but in many cases you need to accomplish this task in your own applications with your code.
Fortunately, .NET has provided all the tools to do this and all you need is to apply these tools.  The process to do this is a bit different in .NET 2.0 because a main object for this process in .NET 1.x is obsolete in .NET 2.0.
Here I give you the steps you need to do this in both .NET 1.x and 2.0.
.NET 1.x

In 1.x you had to follow these steps to validate an XML file against an XSD Schema file.
1.      Read XML file content as String, XmlTextReader or Stream.
2.      Read Schema file content as Stream, TextReader or XmlReader.
3.      Create a new instance of the XmlSchema object.
4.      Set XmlSchema object by calling the XmlSchema.Read() method and passing the content of the Schema file and ValidationEventHandler method address to it.
5.      Create a new instance of XmlValidationReader object.
6.      Set ValidationType for XmlValidationReader object to Schema.
7.      Add your XmlSchema object to XmlValidationReader Schemas collection by calling its Schemas.Add() method.
8.      Add your ValidationEventHandler method address to XmlValidationReader's ValidationEventHandler handler.
9.      Call Read() method of the Reader or Stream object to contain your XML file content in a loop to parse and validate it completely.
10.  Add your logic to the ValidationEventHandler method you created to implement what you want to be done in the validation process.
Here I have written a sample for this process.  Comments show each step in code.  I read my XML file as XmlTextReader and my Schema as Stream objects then got the line and position of each error occurrence in my XML file structure and returned them as an ArrayList of strings.  Full source code of this class has been provided with this article.
Listing 1
private void ValidatingProcess(string XSDPath, string XMLPath)
{
    try
    {
        // 1- Read XML file content
        Reader = new XmlTextReader(XMLPath); 
 
        // 2- Read Schema file content
        StreamReader SR = new StreamReader(XSDPath); 
 
        // 3- Create a new instance of XmlSchema object
        XmlSchema Schema = new XmlSchema();
 
 
        // 4- Set XmlSchema object by calling XmlSchema.Read() method
        Schema = XmlSchema.Read(SR, 
            new ValidationEventHandler(ValidatingReader_ValidationEventHandler)); 
 
        // 5- Create a new instance of XmlValidationReader object
        XmlValidatingReader ValidatingReader = new XmlValidatingReader(Reader);
        // 6- Set ValidationType for XmlValidationReader object
        ValidatingReader.ValidationType = ValidationType.Schema;
        // 7- Add Schema to XmlValidationReader Schemas collection
        ValidatingReader.Schemas.Add(Schema); 
 
        // 8- Add your ValidationEventHandler address to 
        // XmlValidationReader's ValidationEventHandler
        ValidatingReader.ValidationEventHandler += 
            new ValidationEventHandler(ValidatingReader_ValidationEventHandler); 
 
        // 9- Read XML content in a loop
        while (ValidatingReader.Read())
        {/*Empty loop*/} 
 
    }// try
        //Handle exceptions if you want
    catch (UnauthorizedAccessException AccessEx)
    {
        throw AccessEx;
    }// catch
    catch (Exception Ex)
    {
        throw Ex;
    }// catch
}
 
private void ValidatingReader_ValidationEventHandler(object sender,    
    ValidationEventArgs args)
{
    // 10- Implement your logic for each validation iteration
    string strTemp;
    strTemp = "Line: " + this.Reader.LineNumber + " - Position: " + 
        this.Reader.LinePosition + " - " + args.Message; 
 
    this.Results.Add(strTemp);
}
.NET 2.0

Things are a bit different for some steps in .NET 2.0 because there are obsolete objects we used for validation in .NET 1.x.
1.      Read the XML file content as Stream, TextReader or XmlReader.
2.      Read the Schema file content as Stream, TextReader or XmlReader.
3.      Create a new instance of XmlSchema object.
4.      Set XmlSchema object by calling XmlSchema.Read() method and passing the content of Schema file and ValidationEventHandler method address to it.
5.      Create a new instance of XmlReaderSettings object.
6.      Set ValidationType for XmlReaderSettings object to Schema.
7.      Add your XmlSchema object to XmlReaderSettings Schemas collection by calling its Schemas.Add() method.
8.      Add your ValidationEventHandler method address to XmlValidationReader's ValidationEventHandler handler.
9.      Create a new instance of XmlReader object and pass your XML file content (as Stream or a Reader object) and XmlReaderSettings object to it.
10.  Call Read() method of the Reader or Stream object to contain your XML file content in a loop to parse and validate it completely.
11.   Add your logic to ValidationEventHandler method you created to implement what you want to be done in the validation process.
Here I have upgraded the previous code for .NET 2.0:
Listing 2
private void ValidatingProcess(string XSDPath, string XMLPath)
{
    try
    {
        // 1- Read XML file content
        this.Reader = new XmlTextReader(XMLPath);
 
        // 2- Read Schema file content
        StreamReader SR = new StreamReader(XSDPath); 
 
        // 3- Create a new instance of XmlSchema object
        XmlSchema Schema = new XmlSchema();
        // 4- Set Schema object by calling XmlSchema.Read() method
        Schema = XmlSchema.Read(SR, 
            new ValidationEventHandler(ReaderSettings_ValidationEventHandler)); 
 
        // 5- Create a new instance of XmlReaderSettings object
        XmlReaderSettings ReaderSettings = new XmlReaderSettings();    
        // 6- Set ValidationType for XmlReaderSettings object
        ReaderSettings.ValidationType = ValidationType.Schema;                
        // 7- Add Schema to XmlReaderSettings Schemas collection
        ReaderSettings.Schemas.Add(Schema); 
 
        // 8- Add your ValidationEventHandler address to
        // XmlReaderSettings ValidationEventHandler
        ReaderSettings.ValidationEventHandler += 
            new ValidationEventHandler(ReaderSettings_ValidationEventHandler);
 
        // 9- Create a new instance of XmlReader object
        XmlReader objXmlReader = XmlReader.Create(Reader, ReaderSettings);
 
 
        // 10- Read XML content in a loop
        while (objXmlReader.Read())
        { /*Empty loop*/} 
 
    }//try
    // Handle exceptions if you want
    catch (UnauthorizedAccessException AccessEx)
    {
        throw AccessEx;
    }//catch
    catch (Exception Ex)
    {
        throw Ex;
    }//catch
}
 
private void ReaderSettings_ValidationEventHandler(object sender, 
    ValidationEventArgs args)
{
    // 11- Implement your logic for each validation iteration
    string strTemp;
    strTemp = "Line: " + this.Reader.LineNumber + " - Position: " 
        + this.Reader.LinePosition + " - " + args.Message; 
 
    this.Results.Add(strTemp);
}
More about ValidationEventArgs

Your ValidationEventArgs parameter in ValidationEventHandler has some useful properties.
·         Exception: It is an instance of XmlSchemaException object.  This object has several properties that can help you to find more information about the error that has been found in your XML file structure during the validation.
·         Message: The string value of error or warning that has been found in your XML file in current validation iteration.
·         Severity: This is an XmlSeverityType enumeration value and can help you to specify that your validation process has faced an error or warning.

No comments:

Post a Comment