Is there any way to validate an XML file on an XSD schema with the output of the error line numbers?
The XmlReader reader doesn\'t allow line numbers, it shows only
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 ;-)
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);
}
This example is the most complete I've found. It's slower than other validation methods, but it's the best for debugging: It writes validated element for each line and errors found.
To have numbers of lines with errors just add this line in ValidationHandler
method:
Console.WriteLine(args.Exception.LineNumber);
Pay attention that xsd file is specified in the root
tag of the Xml doc.
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);
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
Try this working example. IXmlLineInfo
gets you the line info.
I am getting all the list of errors here, concatenating the Line number, unique id of that specific record, the element name where the error occurred and adding it to a list.
//get the input file here - You can replace this to your local file
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count > 0)
{
var postedFile = httpRequest.Files[0];
//sete the xsd schema path
string xsdPath = HttpContext.Current.Server.MapPath("~/XSD/MyFile.xsd");
//set the XSD schema here
var schema = new XmlSchemaSet();
schema.Add("", xsdPath);
var Message = "";
//validate the xml schema here
XDocument document = XDocument.Load(postedFile.InputStream, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo | LoadOptions.SetBaseUri);
//create a lists to add the error records
List<string> lstErrors = new List<string>();
document.Validate(schema, ValidationEventHandler);
//validate all the errors
document.Validate(schema, (sender, args) =>
{
IXmlLineInfo item = sender as IXmlLineInfo;
if (item != null && item.HasLineInfo())
{
//capture all the details needed here seperated by colons
Message = item.LineNumber + ";" +
(((System.Xml.Linq.XObject)item).Parent.Element("id")).Value + ";" +
((System.Xml.Linq.XElement)item).Name.LocalName + ";" +
args.Message + Environment.NewLine;
//add the error to a list
lstErrors.Add(Message);
}
});
}