XDocument.Validate is always successful
I am not sure it is possible to use the Validate method; if you use a validating XmlReader over the XDocument where ValidationFlags are set up to emit validation warnings, as in
XDocument doc = XDocument.Load("../../XMLFile1.xml");
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(null, "../../XMLSchema1.xsd");
XmlReaderSettings xrs = new XmlReaderSettings();
xrs.ValidationType = ValidationType.Schema;
xrs.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
xrs.Schemas = schemaSet;
xrs.ValidationEventHandler += (o, s) => {
Console.WriteLine("{0}: {1}", s.Severity, s.Message);
};
using (XmlReader xr = XmlReader.Create(doc.CreateReader(), xrs))
{
while (xr.Read()) { }
}
then the ValidationEventHandler does emit a warning for each node it does not find schema information for. So your ValidationEventHandler could check for such warnings. But you might as well simply compare the doc.Root.Name.Namespace with the target namespace of the schemas you have before calling the Validate method.
Invalid XDocument is getting validated
The issue is that both cases 2 and 3 are valid per the schema - your schema doesn't have any opinion on elements in namespaces other than its targetNamespace
.
XmlReader
can return a warning for this, but there's no overload for XDocument
that will do it. The snippet in your linked question uses an XmlReader
wrapper around XDocument
, I can't see why you'd have any issue with doing it this way.
XDocument.Validate not catching all errors against XSD
It seems that XmlReader
stops validation of element on first encountered error. Here is a link to description of old (obsolete) XmlValidatingReader
ValidationEventHandler:
If an element reports a validation error, the rest of the content
model for that element is not validated, however, its children are
validated. The reader only reports the first error for a given
element.
And it seems it is the same with regular XmlReader
(though its documentation does not mention it explicitly).
In first examples errors are either in innermost elements (such as invalid text value of element) or at the last child element, so they are all reported and nothing skipped. However in last example you introduce error at the beginning of root Abc001
element, so the rest of Abc001
content is skipped, together with all errors.
Partial XML file validation using XSD
You have a few issues here:
Validating the entire document succeeds when it should fail.
This happens because the root node is unknown to the schema, and encountering an unknown node is considered a validation warning not a validation error - even if that unknown node is the root element. To enable warnings while validating, you need to set
XmlSchemaValidationFlags.ReportValidationWarnings
. However, there's no way to pass this flag toXDocument.Validate()
. The question XDocument.Validate is always successful shows one way to work around this.Having done this, you must also throw an exception in your validation handler when
ValidationEventArgs.Severity == XmlSeverityType.Warning
.(As for requiring a certain root element in your XSD, this is apparently not possible.)
You need a convenient way to validate elements as well as documents, so you can validate your
<TestConfiguration>
piece.Your XSD and XML are inconsistent.
You XSD specifies that your elements are in the XML namespace
MyApp_ConfigurationFiles
in the linetargetNamespace="MyApp_ConfigurationFiles" elementFormDefault="qualified"
. In fact the XML elements shown in your question are not in any namespace.If the XSD is correct, your XML root node needs to look like:
<Root xmlns="MyApp_ConfigurationFiles">
If the XML is correct, your XSD needs to look like:
<xs:schema id="TestConfiguration"
elementFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
After you have resolved the XSD and XML inconsistency from #3, you can solve issues #1 and #2 by introducing the following extension methods that validate both documents and elements:
public static class XNodeExtensions
{
public static void Validate(this XContainer node, XmlReaderSettings settings)
{
if (node == null)
throw new ArgumentNullException();
using (var innerReader = node.CreateReader())
using (var reader = XmlReader.Create(innerReader, settings))
{
while (reader.Read())
;
}
}
public static void Validate(this XContainer node, XmlSchemaSet schemaSet, XmlSchemaValidationFlags validationFlags, ValidationEventHandler validationEventHandler)
{
var settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags |= validationFlags;
if (validationEventHandler != null)
settings.ValidationEventHandler += validationEventHandler;
settings.Schemas = schemaSet;
node.Validate(settings);
}
}
Then, to validate the entire document, do:
try
{
var xsdDocument = XDocument.Load(xsdFilePath);
var schemaSet = new XmlSchemaSet();
using (var xsdReader = xsdDocument.CreateReader())
schemaSet.Add(XmlSchema.Read(xsdReader, this.XmlSchemaEventHandler));
var xmlDocument = XDocument.Load(xmlFile);
xmlDocument.Validate(schemaSet, XmlSchemaValidationFlags.ReportValidationWarnings, XmlValidationEventHandler);
}
catch (Exception e)
{
Logger.Info("Error parsing XML file: " + xmlFile);
throw new Exception(e.Message);
}
And to validate a specific node, you can use the same extension methods:
XNamespace elementNamespace = "MyApp_ConfigurationFiles";
var elementName = elementNamespace + "TestConfiguration";
try
{
var xsdDocument = XDocument.Load(xsdFilePath);
var schemaSet = new XmlSchemaSet();
using (var xsdReader = xsdDocument.CreateReader())
schemaSet.Add(XmlSchema.Read(xsdReader, this.XmlSchemaEventHandler));
var xmlDocument = XDocument.Load(xmlFile);
var element = xmlDocument.Root.Element(elementName);
element.Validate(schemaSet, XmlSchemaValidationFlags.ReportValidationWarnings, this.XmlValidationEventHandler);
}
catch (Exception e)
{
Logger.Info(string.Format("Error validating element {0} of XML file: {1}", elementName, xmlFile));
throw new Exception(e.Message);
}
Now validating the entire document fails while validating the {MyApp_ConfigurationFiles}TestConfiguration
node succeeds, using the following validation event handlers:
void XmlSchemaEventHandler(object sender, ValidationEventArgs e)
{
if (e.Severity == XmlSeverityType.Error)
throw new XmlException(e.Message);
else if (e.Severity == XmlSeverityType.Warning)
Logger.Info(e.Message);
}
void XmlValidationEventHandler(object sender, ValidationEventArgs e)
{
if (e.Severity == XmlSeverityType.Error)
throw new XmlException(e.Message);
else if (e.Severity == XmlSeverityType.Warning)
throw new XmlException(e.Message);
}
Validating XML documents with XSD correctly
There is no nodes in your XML that can be validated by the schema (namespaces are different). As result it does not report any errors. As far as I know behavior for nodes that are not matched to any schema is allow anything.
You also could set validation options in XmlReaderSettings to allow warnings:
ReportValidationWarnings - Indicates that events should be reported if a validation warning occurs. A warning is typically issued when there is no DTD or XML Schema to validate a particular element or attribute against. The ValidationEventHandler is used for notification.
Check out XmlSchemaSet.Add and HOW TO: Validate an XML Document by Using Multiple Schemas if you expect nodes from multiple namespaces to be present in the XML.
xsd validation limit root element
Result.
Validate() catches only errors, not warnings.
xmlReader has more options to check xml by xsd
If xml and xsd have different namespaces, validate() will be always true.
To fix it you should remove namespaces from both files or write the same namespace.
XDocument.Parse Success or Failure?
Is Try Catch the only way to do this?
There is no TryParse
method for XDocument
, so try-catch
is probably the best bet. Also consider validating your XML against a schema since it will not only check if the XML is well-formed, but also checks for constraints.
You may see: Validation Against XML Schema (XSD) with the XmlValidatingReader
Validate xml against xsd without considering xml namesapce
The namespace is a part of the element name, so there's not much you can do other than ensure they are correct.
If all the element namespaces are supposed to be the same, you could set the namespace on all your elements before validating:
XNamespace ns = "urn:testnew/properties/v1.0";
foreach (var element in xmlDocument.Descendants())
{
element.Name = ns + element.Name.LocalName;
}
xmlDocument.Validate(...);
Unfortunately, if the namespace doesn't match then the XML is valid according to the schema (provided it is well formed) as the schema simply doesn't apply to the elements. The validation can raise a warning to say the elements aren't recognised, though it's not possible to pass this flag via the XDocument.Validate
extension method (as far as I can tell!).
This question shows an alternate validation method using XmlReader
and XmlReaderSettings
that would allow you to capture warnings if the schema does not recognise elements.
Related Topics
How to Connect to ASP.NET Development Server Issue
Checking File/Folder Access Permission
Internal VS. Private Access Modifiers
Extension Methods Syntax VS Query Syntax
How to Generate a Hashcode from a Byte Array in C#
How to Prevent a Windows from Being Moved
How to Clear All Controls on a Form C#
Xdocument.Validate Is Always Successful
How to Avoid "Too Many Parameters" Problem in API Design
Creating PDF Files at Runtime in C#
Convert Rows from a Data Reader into Typed Results