174

I have an XML file with a specified schema location such as this:

xsi:schemaLocation="someurl ..\localSchemaPath.xsd"

I want to validate in C#. Visual Studio, when I open the file, validates it against the schema and lists errors perfectly. Somehow, though, I can't seem to validate it automatically in C# without specifying the schema to validate against like so:

XmlDocument asset = new XmlDocument();

XmlTextReader schemaReader = new XmlTextReader("relativeSchemaPath");
XmlSchema schema = XmlSchema.Read(schemaReader, SchemaValidationHandler);

asset.Schemas.Add(schema);

asset.Load(filename);
asset.Validate(DocumentValidationHandler);

Shouldn't I be able to validate with the schema specified in the XML file automatically ? What am I missing ?

xav
  • 5,452
  • 7
  • 48
  • 57
jfclavette
  • 3,457
  • 2
  • 20
  • 17
  • 1
    Refer the MSDN sample : http://msdn.microsoft.com/en-us/library/system.xml.schema.validationeventargs.severity.aspx –  Jan 17 '12 at 13:19

5 Answers5

178

You need to create an XmlReaderSettings instance and pass that to your XmlReader when you create it. Then you can subscribe to the ValidationEventHandler in the settings to receive validation errors. Your code will end up looking like this:

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

public class ValidXSD
{
    public static void Main()
    {

        // Set the validation settings.
        XmlReaderSettings settings = new XmlReaderSettings();
        settings.ValidationType = ValidationType.Schema;
        settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
        settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
        settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
        settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);

        // Create the XmlReader object.
        XmlReader reader = XmlReader.Create("inlineSchema.xml", settings);

        // Parse the file. 
        while (reader.Read()) ;

    }
    // Display any warnings or errors.
    private static void ValidationCallBack(object sender, ValidationEventArgs args)
    {
        if (args.Severity == XmlSeverityType.Warning)
            Console.WriteLine("\tWarning: Matching schema not found.  No validation occurred." + args.Message);
        else
            Console.WriteLine("\tValidation error: " + args.Message);

    }
}
Kiquenet
  • 14,494
  • 35
  • 148
  • 243
Chris McMillan
  • 1,954
  • 1
  • 13
  • 5
  • 4
    +1 although should update to use `using` clause for completeness :) – IAbstract Dec 10 '11 at 02:26
  • 62
    If you're looking to compare against an XSD file add the following line to the above code: settings.Schemas.Add("YourDomainHere", "yourXSDFile.xsd"); – Jeff Fol Apr 14 '12 at 19:05
  • 8
    To get Line# and position# of the error simply use: args.Exception.LineNumber... in ValidationCallBack – user610064 Dec 21 '12 at 14:29
  • 1
    What if the schema i have doesn't have a namespace? – tree May 13 '14 at 20:58
  • What's means ***inlineSchema*** for the _xml file_ ? And if I have **xml file** with `xmlns:xsi, xmlns and xsi:schemaLocation`, and I have **xsd file** with `targetNamespace, xmlns, and xmlns:xs` ? [full files](https://gist.github.com/kiquenet/86090779650975b7426a7f798174eef6) – Kiquenet Mar 07 '17 at 14:56
  • 1
    Using ***lambda***, better IMHO, more _clarity code_ `settings.ValidationEventHandler += (o, args) => { errors = true; // More code };` – Kiquenet Mar 07 '17 at 15:18
  • you are looking for ProcessInlineSchema. Defined in XML itself. Above solution is correct for it. – Vaibhav.Inspired May 10 '20 at 17:23
  • When compare against an XSD file, you can use : settings.Schemas.Add("xml file schema namespace", "path to xsd file"); If the there is no "xml file schema namespace", you can use : settings.Schemas.Add(null, "path to xsd file"); – Rajitha Kithuldeniya Sep 22 '21 at 08:28
122

A simpler way, if you are using .NET 3.5, is to use XDocument and XmlSchemaSet validation.

XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(schemaNamespace, schemaFileName);

XDocument doc = XDocument.Load(filename);
string msg = "";
doc.Validate(schemas, (o, e) => {
    msg += e.Message + Environment.NewLine;
});
Console.WriteLine(msg == "" ? "Document is valid" : "Document invalid: " + msg);

See the MSDN documentation for more assistance.

saluce
  • 13,035
  • 3
  • 50
  • 67
  • 2
    That method requires you know the schema beforehand rather than taking the inline schema from the xml. – user692942 Jun 25 '13 at 09:46
  • this works fine but throws error when xml document contains some html tag like my new catalog.... in above case html tags like "" creates an issue as it is the value of "" ... how to validate it – Anil Purswani Oct 08 '14 at 09:27
  • 6
    @AnilPurswani: If you wish to put HTML into an XML document, you need to wrap it in CDATA. `<![CDATA[my new catalog....]]>` is the correct way to do that. – p0lar_bear May 20 '15 at 14:38
  • Simple and elegant! This works very well when validating against a fixed schema set (which is our case, and a big one with multiple folders and files). I'm already thinking about caching the XmlSchemaSet to be reused between calls to the Validator. Thanks a lot! – Adail Retamal Jul 30 '19 at 01:37
  • 1
    It does not work when the default namespace in XSD is different than in XML. – Sylwester Santorowski Aug 31 '22 at 11:05
24

personally I favor validating without a callback:

public bool ValidateSchema(string xmlPath, string xsdPath)
{
    XmlDocument xml = new XmlDocument();
    xml.Load(xmlPath);

    xml.Schemas.Add(null, xsdPath);

    try
    {
        xml.Validate(null);
    }
    catch (XmlSchemaValidationException)
    {
        return false;
    }
    return true;
}

(see Timiz0r's post in Synchronous XML Schema Validation? .NET 3.5)

Community
  • 1
  • 1
FrankyHollywood
  • 1,497
  • 19
  • 18
21

The following example validates an XML file and generates the appropriate error or warning.

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

public class Sample
{

    public static void Main()
    {
        //Load the XmlSchemaSet.
        XmlSchemaSet schemaSet = new XmlSchemaSet();
        schemaSet.Add("urn:bookstore-schema", "books.xsd");

        //Validate the file using the schema stored in the schema set.
        //Any elements belonging to the namespace "urn:cd-schema" generate
        //a warning because there is no schema matching that namespace.
        Validate("store.xml", schemaSet);
        Console.ReadLine();
    }

    private static void Validate(String filename, XmlSchemaSet schemaSet)
    {
        Console.WriteLine();
        Console.WriteLine("\r\nValidating XML file {0}...", filename.ToString());

        XmlSchema compiledSchema = null;

        foreach (XmlSchema schema in schemaSet.Schemas())
        {
            compiledSchema = schema;
        }

        XmlReaderSettings settings = new XmlReaderSettings();
        settings.Schemas.Add(compiledSchema);
        settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
        settings.ValidationType = ValidationType.Schema;

        //Create the schema validating reader.
        XmlReader vreader = XmlReader.Create(filename, settings);

        while (vreader.Read()) { }

        //Close the reader.
        vreader.Close();
    }

    //Display any warnings or errors.
    private static void ValidationCallBack(object sender, ValidationEventArgs args)
    {
        if (args.Severity == XmlSeverityType.Warning)
            Console.WriteLine("\tWarning: Matching schema not found.  No validation occurred." + args.Message);
        else
            Console.WriteLine("\tValidation error: " + args.Message);

    }
}

The preceding example uses the following input files.

<?xml version='1.0'?>
<bookstore xmlns="urn:bookstore-schema" xmlns:cd="urn:cd-schema">
  <book genre="novel">
    <title>The Confidence Man</title>
    <price>11.99</price>
  </book>
  <cd:cd>
    <title>Americana</title>
    <cd:artist>Offspring</cd:artist>
    <price>16.95</price>
  </cd:cd>
</bookstore>

books.xsd

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns="urn:bookstore-schema"
    elementFormDefault="qualified"
    targetNamespace="urn:bookstore-schema">

 <xsd:element name="bookstore" type="bookstoreType"/>

 <xsd:complexType name="bookstoreType">
  <xsd:sequence maxOccurs="unbounded">
   <xsd:element name="book"  type="bookType"/>
  </xsd:sequence>
 </xsd:complexType>

 <xsd:complexType name="bookType">
  <xsd:sequence>
   <xsd:element name="title" type="xsd:string"/>
   <xsd:element name="author" type="authorName"/>
   <xsd:element name="price"  type="xsd:decimal"/>
  </xsd:sequence>
  <xsd:attribute name="genre" type="xsd:string"/>
 </xsd:complexType>

 <xsd:complexType name="authorName">
  <xsd:sequence>
   <xsd:element name="first-name"  type="xsd:string"/>
   <xsd:element name="last-name" type="xsd:string"/>
  </xsd:sequence>
 </xsd:complexType>

</xsd:schema>
matt
  • 333
  • 2
  • 10
  • 23
Soroush
  • 425
  • 4
  • 6
13

I had do this kind of automatic validation in VB and this is how I did it (converted to C#):

XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags = settings.ValidationFlags |
                           Schema.XmlSchemaValidationFlags.ProcessSchemaLocation;
XmlReader XMLvalidator = XmlReader.Create(reader, settings);

Then I subscribed to the settings.ValidationEventHandler event while reading the file.

saluce
  • 13,035
  • 3
  • 50
  • 67
Welbog
  • 59,154
  • 9
  • 110
  • 123