Getting started with XSD validation with .NET Getting started with XSD validation with .NET xml xml

Getting started with XSD validation with .NET


Rather than using the XDocument.Validate extension method, I would use an XmlReader which can be configured to process an inline schema via XmlReaderSettings. You could do some thing like the following code.

public void VerifyXmlFile(string path){    // configure the xmlreader validation to use inline schema.    XmlReaderSettings config = new XmlReaderSettings();    config.ValidationType = ValidationType.Schema;    config.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;    config.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;    config.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;    config.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);    // Get the XmlReader object with the configured settings.    XmlReader reader = XmlReader.Create(path, config);    // Parsing the file will cause the validation to occur.    while (reader.Read()) ;}private void ValidationCallBack(object sender, ValidationEventArgs vea){    if (vea.Severity == XmlSeverityType.Warning)        Console.WriteLine(            "\tWarning: Matching schema not found.  No validation occurred. {0}",            vea.Message);    else        Console.WriteLine("\tValidation error: {0}", vea.Message);}

The code above assumes the following using statements.

using System.Xml;using System.Xml.Schema;

Just to keep this simple I did not return a boolean or a collection of validation errors, you could easily modify this to do so.

Note: I modified your config.xml and config.xsd to get them to validate. These are the changes I made.

config.xsd:

<xs:element maxOccurs="unbounded" name="levelVariant">

config.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="config.xsd">


Following is out of a working sample:

Usage:

XMLValidator val = new XMLValidator();if (!val.IsValidXml(File.ReadAllText(@"d:\Test2.xml"), @"D:\Test2.xsd"))   MessageBox.Show(val.Errors);

Class:

public class CXmlValidator{    private int nErrors = 0;    private string strErrorMsg = string.Empty;    public string Errors { get { return strErrorMsg; } }    public void ValidationHandler(object sender, ValidationEventArgs args)    {        nErrors++;        strErrorMsg = strErrorMsg + args.Message + "\r\n";    }    public bool IsValidXml(string strXml/*xml in text*/, string strXsdLocation /*Xsd location*/)    {        bool bStatus = false;        try        {            // Declare local objects            XmlTextReader xtrReader = new XmlTextReader(strXsdLocation);            XmlSchemaCollection xcSchemaCollection = new XmlSchemaCollection();            xcSchemaCollection.Add(null/*add your namespace string*/, xtrReader);//Add multiple schemas if you want.            XmlValidatingReader vrValidator = new XmlValidatingReader(strXml, XmlNodeType.Document, null);            vrValidator.Schemas.Add(xcSchemaCollection);            // Add validation event handler            vrValidator.ValidationType = ValidationType.Schema;            vrValidator.ValidationEventHandler += new ValidationEventHandler(ValidationHandler);            //Actual validation, read conforming the schema.            while (vrValidator.Read()) ;            vrValidator.Close();//Cleanup            //Exception if error.            if (nErrors > 0) { throw new Exception(strErrorMsg); }            else { bStatus = true; }//Success        }        catch (Exception error) { bStatus = false; }        return bStatus;    }}

The above code validates following xml(code3) against xsd(code4).

<!--CODE 3 - TEST1.XML--><address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test1.xsd"> <name>My Name</name><street>1, My Street Address</street><city>Far</city><country>Mali</country></address><!--CODE 4 - TEST1.XSD--><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:element name="address"><xs:complexType><xs:sequence><xs:element name="name" type="xs:string"/><xs:element name="street" type="xs:string"/><xs:element name="city" type="xs:string"/><xs:element name="country" type="xs:string"/></xs:sequence></xs:complexType></xs:element></xs:schema>

In validating against your xml/xsd I get of errors different than yours; I think this can help you continue(add/remove xml elements) from here:

Errors

You may also try the reverse process; try generating the schema from your xml and compare with your actual xsd - see the difference; and the easiest way to do that is to use generate schema using VS IDE. Following is how you'd do that:

Create XSD from XML

Hope this helps.

--EDIT--

This is upon John's request, please see updated code using non deprecated methods:

public bool IsValidXmlEx(string strXmlLocation, string strXsdLocation){    bool bStatus = false;    try    {        // Declare local objects        XmlReaderSettings rs = new XmlReaderSettings();        rs.ValidationType = ValidationType.Schema;        rs.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation | XmlSchemaValidationFlags.ReportValidationWarnings;        rs.ValidationEventHandler += new ValidationEventHandler(rs_ValidationEventHandler);        rs.Schemas.Add(null, XmlReader.Create(strXsdLocation));        using (XmlReader xmlValidatingReader = XmlReader.Create(strXmlLocation, rs))        { while (xmlValidatingReader.Read()) { } }        ////Exception if error.        if (nErrors > 0) { throw new Exception(strErrorMsg); }        else { bStatus = true; }//Success    }    catch (Exception error) { bStatus = false; }    return bStatus;}void rs_ValidationEventHandler(object sender, ValidationEventArgs e){    if (e.Severity == XmlSeverityType.Warning) strErrorMsg += "WARNING: " + Environment.NewLine;    else strErrorMsg += "ERROR: " + Environment.NewLine;    nErrors++;    strErrorMsg = strErrorMsg + e.Exception.Message + "\r\n";}

Usage:

if (!val.IsValidXmlEx(@"d:\Test2.xml", @"D:\Test2.xsd"))                MessageBox.Show(val.Errors);            else                MessageBox.Show("Success");

Test2.XML

<?xml version="1.0" encoding="utf-8" ?><config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test2.xsd">  <levelVariant>    <filePath>SampleVariant</filePath>  </levelVariant>  <levelVariant>    <filePath>LegendaryMode</filePath>  </levelVariant>  <levelVariant>    <filePath>AmazingMode</filePath>  </levelVariant></config>

Test2.XSD (Generated from VS IDE)

<?xml version="1.0" encoding="utf-8" ?><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">  <xs:element name="config">    <xs:complexType>      <xs:sequence>        <xs:element maxOccurs="unbounded" name="levelVariant">          <xs:complexType>            <xs:sequence>              <xs:element name="filePath" type="xs:anyURI">              </xs:element>            </xs:sequence>          </xs:complexType>        </xs:element>      </xs:sequence>    </xs:complexType>  </xs:element></xs:schema>

This is guaranteed to work!


Your code to extract the schema location looks weird. Why do you get the value of the xmlns attribute and concatenate it with the value of the xsi:noNamespaceSchemaLocation attribute? The exception is caused by the fact that you cannot specify a prefix in a call to Attributes; you need to specify the desired XNamespace.

Try this (untested):

// Load documentXDocument doc = XDocument.Load("file.xml");// Extract value of xsi:noNamespaceSchemaLocationXNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";string schemaURI = (string)doc.Root.Attribute(xsi + "noNamespaceSchemaLocation");// Create schema setXmlSchemaSet schemas = new XmlSchemaSet();schemas.Add("Schemas", schemaURI);// Validatedoc.Validate(schemas, (o, e) =>                      {                          Console.WriteLine("{0}", e.Message);                      });