Validating XML on XSD with the error line numbers

Since @chris-watts suggested to post my comment again as answer. Here it is.

The document only has line number information if it was loaded with the appropriate flags:

var opts = LoadOptions.PreserveWhitespace 
         | LoadOptions.SetLineInfo;
XDocument doc = XDocument.Load(fileStream, opts); 

Woah, rather tricky this XSD stuff, I'm also new :)

As it was said before the position information gets lost in the XmlDocument.

I got it finally running using the XmlReader in combination with XmlDocument:

// xmlStream and xsdStream are open streams that 
// point to the respective xml and xsd files
public void ReadAndVerify(Stream xmlStream, Stream xsdStream)
{
    // Read the scheme validation and compile it
    XmlSchemaSet schemaSet = new XmlSchemaSet();

    using (XmlReader r = XmlReader.Create(xsdStream)) 
    {
        schemaSet.Add(XmlSchema.Read(r, null));
    }

    schemaSet.CompilationSettings = new XmlSchemaCompilationSettings();
    schemaSet.Compile();

    // Setup the settings for the reader.
    // This includes the previously compiled schema
    XmlReaderSettings settings = new XmlReaderSettings();
    settings.CloseInput = true;

    // This is the callback method see below
    settings.ValidationEventHandler += ValidationEventHandler;

    settings.ValidationType = ValidationType.Schema;
    settings.Schemas = schemaSet;  // <-- here the schema is set

    // To be honest, this is cut'n'paste. Not sure which flags are really required.
    settings.ValidationFlags =
            XmlSchemaValidationFlags.ReportValidationWarnings |
    XmlSchemaValidationFlags.ProcessIdentityConstraints |
    XmlSchemaValidationFlags.ProcessInlineSchema |
    XmlSchemaValidationFlags.ProcessSchemaLocation;

    // Now the validating reader is created
    using (XmlReader validatingReader = XmlReader.Create(xmlStream, settings)) 
    {

        // This has to be done BEFORE the validating while loop
        XmlDocument x = new XmlDocument();
        x.Load(validatingReader);

        // This is the validation loop
        while (validatingReader.Read()) ;

        // This is the client code that actually uses the XmlDocument nodes.
        XmlNode node = x[RootNode];
        ReadAllParameters(node);
    }
}

And the ValidationEventHandler:

private void ValidationEventHandler(object sender, ValidationEventArgs e)
{
    // This actually works with this approach
    string text = $"[Line: {e.Exception?.LineNumber}, Column: {e.Exception?.LinePosition}]: {e.Message}";
    switch (e.Severity) {
        case XmlSeverityType.Error:
            Logger.Error(text);
            break;
        case XmlSeverityType.Warning:
            Logger.Warn(e.Message);
            break;
    }
}

Life can be so simple ;-)


You can use XMLStarlet. That supports validating, and provides error line numbers:

$ xml val -e --xsd file.xsd file.xml
file.xml:8: Element 'atitle': This element is not expected. Expected is ( title ).
file.xml - invalid

See the original MSDN example:

http://msdn.microsoft.com/en-us/library/ms172454.aspx

ValidationEventHandler's ValidationEventArgs argument has Exception.LineNumber:

private void SchemaValidationEventHandler(object sender, ValidationEventArgs e) {
    Console.WriteLine("XML {0}: {1} (Line {2})",
                         e.Severity,
                         e.Message,
                         e.Exception.LineNumber);
}

Tags:

C#

.Net