Validation Example
This example will demonstrate the validation of an XML document stored in a string variable against an XML schema. The schema is specified as a reference. This scenario represents a common design pattern encountered in inter-enterprise communication via Web services: business documents are transferred in XML format, and validated by both trading partners against a standard schema that is stored in a mutually accessible location.
The XML document being validated in this example (see Listing 1) represents a one-record result set from a SQL Server 2000 query that uses the FOR XML EXPLICIT syntax to generate a well-formed XML document. (See my InformIT article, "SQL Server XML in ADO.NET" for more about this topic.)
Listing 1XML Document Containing Recipe Data
<rns:Recipes xmlns:xsd="http://www.w3.org/2001/XMLSchema-instance" xmlns:rns='xsdRecipes' xsd:schemaLocation='xsdRecipes recipes.xsd'> <Recipe> <Title>Easy Shrimp Dip</Title> <Description>A simple dip, perfect for raw vegetables.</Description> <PrepTime>10</PrepTime> <LeadTime>480</LeadTime> <Contributor>Scott Loban</Contributor> <ContDate>2001-11-13T00:00:00</ContDate> <Source>Original</Source> <Copyright>Copyright (C) 2001 Scott Loban</Copyright> <Servings>12</Servings> <Ingredient> <Quantity>2.0000</Quantity> <Units>each</Units> <Desc>cans Bay Shrimp (drained)</Desc> </Ingredient> <Ingredient> <Quantity>8.0000</Quantity> <Units>ounce</Units> <Desc>Cream Cheese (softened)</Desc> </Ingredient> <Ingredient> <Quantity>2.0000</Quantity> <Units>teaspoon</Units> <Desc>Lemon Juice</Desc> </Ingredient> <Ingredient> <Quantity>0.2500</Quantity> <Units>cup</Units> <Desc>Minced Bell Pepper</Desc> </Ingredient> <Ingredient> <Quantity>0.2500</Quantity> <Units>cup</Units> <Desc>Minced Celery</Desc> </Ingredient> <Ingredient> <Quantity>1.0000</Quantity> <Units>each</Units> <Desc>pkg. Italian Dressing Mix</Desc> </Ingredient> <Ingredient> <Quantity>8.0000</Quantity> <Units>ounce</Units> <Desc>Sour Cream</Desc> </Ingredient> <Keyword> <Text>Appetizer</Text> </Keyword> <Keyword> <Text>Dip</Text> </Keyword> <Keyword> <Text>Seafood</Text> </Keyword> <Instruction> <StepOrder>1</StepOrder> <Step>Cream together cream cheese and sour cream.</Step> </Instruction> <Instruction> <StepOrder>2</StepOrder> <Step>Add remaining ingredients and mix thoroughly.</Step> </Instruction> <Instruction> <StepOrder>3</StepOrder> <Step>Chill overnight.</Step> </Instruction> <Instruction> <StepOrder>4</StepOrder> <Step>Serve with raw vegetables and/or crackers.</Step> </Instruction> </Recipe> </rns:Recipes>
The XML Schema (see Listing 2) defines the elements (along with their attributes, data types, and ranges) that will be considered legal for documents that are valid with respect to this schema. For this example, the schema is referenced in the XML document through the schemaLocation attribute.
Listing 2XML Schema for Recipes
<?xml version="1.0" ?> <xsd:schema xmlns="xsdRecipes" targetNamespace="xsdRecipes" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:annotation> <xsd:documentation xml:lang="en"> XML schema for recipe data. Part of Scott Lobans .NET Reference Implementation. Copyright (C) 2001, Scott Loban. </xsd:documentation> </xsd:annotation> <xsd:element name="Recipes"> <xsd:complexType> <xsd:sequence> <xsd:element name="Recipe" type="recipe" minOccurs="0" maxOccurs="unbounded" /> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:complexType name="recipe"> <xsd:sequence> <xsd:element name="Title" type="xsd:string" /> <xsd:element name="Description" type="xsd:string" minOccurs="0" /> <xsd:element name="PrepTime" minOccurs="0"> <xsd:simpleType> <xsd:restriction base="xsd:integer"> <xsd:minInclusive value="0" /> </xsd:restriction> </xsd:simpleType> </xsd:element> <xsd:element name="LeadTime" minOccurs="0"> <xsd:simpleType> <xsd:restriction base="xsd:integer"> <xsd:minInclusive value="0" /> </xsd:restriction> </xsd:simpleType> </xsd:element> <xsd:element name="Contributor" type="xsd:string" minOccurs="0" /> <xsd:element name="ContDate" type="xsd:dateTime" minOccurs="0" /> <xsd:element name="Source" type="xsd:string" minOccurs="0" /> <xsd:element name="Copyright" type="xsd:string" minOccurs="0" /> <xsd:element name="Servings"> <xsd:simpleType> <xsd:restriction base="xsd:integer"> <xsd:minInclusive value="1" /> </xsd:restriction> </xsd:simpleType> </xsd:element> <xsd:element name="Ingredient" type="ingredient" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="Keyword" type="keyword" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="Instruction" type="instruction" minOccurs="0" maxOccurs="unbounded" /> <xsd:element name="Picture" type="xsd:anyURI" minOccurs="0" maxOccurs="unbounded" /> </xsd:sequence> </xsd:complexType> <xsd:complexType name="ingredient"> <xsd:sequence> <xsd:element name="Quantity" type="xsd:decimal" /> <xsd:element name="Units" type="xsd:string" /> <xsd:element name="Desc" type="xsd:string" /> </xsd:sequence> </xsd:complexType> <xsd:complexType name="keyword"> <xsd:sequence> <xsd:element name="Text" type="xsd:string" /> </xsd:sequence> </xsd:complexType> <xsd:complexType name="instruction"> <xsd:sequence> <xsd:element name="StepOrder" type="xsd:integer" minOccurs="0" /> <xsd:element name="Step" type="xsd:string" /> </xsd:sequence> </xsd:complexType> </xsd:schema>
Microsoft .NET provides the XmlValidatingReader class, a descendent of XmlReader that provides for validation against DTDs, XDR schemas, and XML schemas. The ValidationType property of this class accepts a ValidationType enumone of Auto, None, DTD, Schema (XML Schema), or XDR (XDR Schema). A ValidationType of None creates a non-validating parser, whereas Auto uses DTD or schema information found in the XML document, if any, or creates a non-validating parser, otherwise.
When implementing validation in .NET, the validation mechanism consists of the interaction of two methods (see Listing 3): one that instantiates the required classes and executes the validation process, and a second callback method that is called each time a validation event occurs.
Listing 3Validation Against the XML Schema
bool g_bValidationState = true; public bool IsValid(string sXML) { /* --- Set up the XML Parser Context --- */ XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable()); nsmgr.AddNamespace("rns","urn:xsdRecipes"); XmlParserContext cntxt = new XmlParserContext(null, nsmgr, null, XmlSpace.None); /* --- Create the Reader --- */ XmlValidatingReader v = new XmlValidatingReader(sXml, XmlNodeType.Document, cntxt); v.ValidationType = ValidationType.Auto; /* --- Set the Event Handler for the Reader --- */ v.ValidationEventHandler += new ValidationEventHandler(MyValidationEventHandler); /* --- Validate the XML --- */ v.Read(); /* --- Clean up --- */ v.Close(); return g_bValidationState; } private static void MyValidationEventHandler(object sender, ValidationEventArgs evt) { g_bValidationState = false; // Log and/or Report Error(s) // evt.Severity (XmlSeverityType enum) // evt.Message (string) // evt.Exception (XmlSchemaException class) }
After some housekeeping in the IsValid method that sets up the class instances for the namespace, parser context, and reader, the validation itself occurs in the following single line:
v.Read();The validating reader will go through the entire string sXML and will call the method MyValidationEventHandler each time a validation event occurs. You must provide the code for this event handler to do appropriate error-handling and/or logging. The ValidationEventArgs object that is passed to the event handler has the following properties that you can use for reporting and to determine the appropriate response to the error:
Severity: an enum, either Error or Warning
Message: a string containing the event description
Exception: an XmlSchemaException object that contains detailed information about the event
This article demonstrates validating a dynamic XML document against an XML schema. Validation against DTDs and XDR schemas is basically the same. Because the Auto ValidationType is used, the code provided will also work with a DTD or XDR schema, if the appropriate schema reference is provided in the subject XML document.
There are a couple of interesting advanced topics related to XML validation that go beyond the scope of this article. The first of these is the inline schema, in which the schema is embedded in the subject XML document. The second is schema caching. In production environments, in which thousands of documents are exchanged every day, it may be desirable to store schemas in memory, so that they do not have to be read from disk and/or across the network each time. XmlValidatingReader supports both of these. Interested readers can see the MSDN library for documentation and code samples.