The XmlTextReader covered in the previous section is an extremely fast and efficient parser, but it does have the downside of not doing any validation of the XML document. The .NET XML architecture provides a higher-level parser called XmlValidatingReader that provides this functionality. We will investigate the XmlValidatingReader in this section and use it to create an example that checks the validity of a document using a DTD.
The XmlValidatingReader doesn't parse text directly. Instead, it relies on another XmlReader like XmlTextReader for that lower-level functionality. Because we've just covered the XmlTextReader in the last section, that is the parser we'll use for our example. Therefore, first a standard XmlTextReader is constructed.
XmlTextReader xmlTextReader = new XmlTextReader("example.xml");
Next, we create an XmlValidatingReader. Instead of passing the name of the file to be parsed into the XmlValidatingReader constructor, the XmlTextReader that will actually do the text parsing is passed into the constructor.
xmlValidatingReader xmlValidatingReader = new XmlValidatingReader(xmlTextReader);
Now we're ready to tell the XmlValidatingReader what type of validation we want to have done to the document. This is done by setting the ValidationType property of the XmlValidatingReader to the desired validation type. The XmlValidatingReader supports five types of validation:
Note
In addition to supporting the W3C XSD schema standard, the Microsoft XML Data Reduced (XDR) schema standard is supported. The XDR schema standard is based on early W3C work on schemas. Although it is similar to XSD schemas, it is not compatible.
For our example, we will validate our document against a DTD.
xmlValidatingReader.ValidationType = ValidationType.DTD;
Validation handling is done with an event-based architecture, similar to SAX. The document is still parsed on demand as it was with the XmlTextReader. However, in order to receive notification that a validation error has occurred, a callback must be registered with the XmlValidatingReader. This is done by first generating a helper class that will be used to construct the callback. Because events are used to signal validation, a Boolean variable called success will be added to the class to keep track of whether a validation error has occurred:
class ValidationHelper { public bool success = true; }
Now we're ready to add the event handler. Under .NET, an event callback is called a delegate and is constructed from a reference to a method in a class. Therefore, a method to be called whenever a validation event needs to be added. This method takes two arguments.
First, an object is passed to the handler containing the object that sent the event.
Second, a ValidationEventArgs object is passed with information on the validation event that triggered the call. The ValidationEventArgs object contains a string property called message that includes the relevant validation error.
For this example, the event handler will signal that an error has occurred by writing the error message to the console. Finally, the event handler sets the success variable to false so that it can be checked later.
public void ValidationCallBack (object sender, ValidationEventArgs args) { Console.WriteLine("Validation error:"); Console.WriteLine(args.Message); Console.WriteLine(); success = false; }
Now that the validation handler has been constructed, it needs to be registered in the main program. This is done in .NET by constructing a ValidationEventHandler object that refers to the method that is to be called. First, an instance of the ValidationHelper class is created. Then a reference to the ValidationCallBack method is added to the list of handlers to be called by the XmlValidatingReader whenever a validation error occurs:
ValidationHelper validationHelper = new ValidationHelper(); xmlValidatingReader.ValidationEventHandler += new ValidationEventHandler (validationHelper.ValidationCallBack);
Now that the mechanism for reporting errors has been put in place, we can go ahead and start parsing the document. This is done using the same method as the XmlTextReader: calling the Read method of the XmlValidatingReader object until there are no remaining nodes. In this case, we are only interested in validation, so a while loop is used to cycle over every node without acting on any of the nodes. This will work because validation errors are signaled as events to the ValidationEventHandler.
while ( xmlValidatingReader.Read() ) { }
Now that the entire document has been parsed, we want to report back to the user whether validation was successful. Because we have a reference to the ValidationHelper object, all that needs to be done is to check the Boolean success variable inside that object and use it to report whether validation was successful or unsuccessful.
if (validationHelper.success == true) Console.WriteLine("Validation was successful"); else Console.WriteLine("Validation was unsuccessful");
The full C# code to this example is shown in Listing 17.4, and the equivalent Visual Basic .NET code is shown in Listing 17.5. If the sample document at the beginning of the chapter is used with this example, "Validation was successful" is reported. However, if we modify the example slightly to be invalid, as shown in Listing 17.6, the following validation errors are reported:
Validation error: The required attribute 'href' is missing. An error occurred at file: file://src/XmlValidatingReader/bin/Debug/example2.xml(18, 4). Validation error: Element 'parsers' has invalid content. Expected 'parser'. An error occurred at file:file://src/XmlValidatingReader/bin/Debug/example2.xml(25, 4). Validation error: The 'extra' element is not declared. An error occurred at file://src/XmlValidatingReader/bin/Debug/example2.xml(25, 4). Validation was unsuccessful
3.17.76.218