XDocument.Validate is always successful - c#

I have a schema file which does not define any target namespaces, i.e. its definition looks like this:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<!--Elements, attributes, etc. -->
</xs:schema>
The corresponding XML looks like this:
<Documents p1:CRC="0" p1:Date="1900-01-01T01:01:01+01:00" p1:Name="Test" p1:Status="new" xmlns:p1="http://www.tempuri.org/pdms.xsd" xmlns="http://www.tempuri.org/pdms.xsd">
<p1:Document p1:Date="2010-12-23T07:59:45" p1:ErrorCode="0" p1:ErrorMessage="" p1:Number="TEST00001" p1:Status="new"/>
</Documents>
Validation of this XML against the schema with e.g. Altova XMLSpy or Oxygen XML Editor fails.
However my validation in C# (.NET 4.0) does not fail. The XML is processed as an XDocument object. If I have understood correctly then XDocument.Validate() does a lax validation if no namespace is found in the schema. Thus the validation does not fail. But then how can I implement a "strict" validation for XDocument?
This is how I try to validate the XML:
public static void ValidateXml(XDocument xml, string xsdFilename) {
XmlReaderSettings settings = new XmlReaderSettings();
XmlSchemaSet schemaSet = new XmlSchemaSet();
schemaSet.Add(string.empty, xsdFilename);
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
settings.ValidationEventHandler += new ValidationEventHandler(ValidationCallback);
xml.Validate(schemaSet, ValidationCallback);
}
private static void ValidationCallback(object sender, ValidationEventArgs args) {
if (args.Severity == XmlSeverityType.Warning) {
// Do warning stuff...
} else if (args.Severity == XmlSeverityType.Error) {
// Do error stuff...
}
}

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.

Related

How to resolve System.Xml.Schema.XmlSchemaValidationException

I am trying to validate XML using an online XSD. Here is my current code for my controller:
using System;
using System.IO;
using System.Net;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using Microsoft.AspNetCore.Mvc;
namespace EINV.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class XmlController : Controller
{
[HttpPost]
public IActionResult ValidateXml2(IFormFile xmlFile, string xsdUrl)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.XmlResolver = new XmlXsdResolver(); // Need this for resolving include and import
settings.ValidationType = ValidationType.Schema; // This might not be needed, I am using same settings to validate the input xml
//settings.DtdProcessing = DtdProcessing.Parse; // I have an include that is dtd. maybe I should prohibit dtd after I compile the xsd files.
settings.Schemas.Add(null, xsdUrl); // https://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd
settings.Schemas.Compile();
settings.ValidationType = ValidationType.Schema;
XmlReader reader = XmlReader.Create(xmlFile.OpenReadStream(), settings, "https://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/");
XmlDocument document = new XmlDocument();
document.Load(reader);
ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler);
// the following call to Validate succeeds.
document.Validate(eventHandler);
// Load the XML file into an XmlDocument
return Ok();
}
protected class XmlXsdResolver : XmlUrlResolver
{
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
{
return base.GetEntity(absoluteUri, role, ofObjectToReturn);
}
}
private void ValidationEventHandler(object? sender, ValidationEventArgs? e)
{
if (e?.Severity == XmlSeverityType.Error)
{
throw new Exception("XML validation error: " + e.Message);
}
}
}
}
I have referenced several other posts in trying to resolve this, such as the following:
How can I resolve the schemaLocation attribute of an .XSD when all of my .XSD's are stored as resources?
Compiling two embedded XSDs: error "Cannot resolve 'schemaLocation' attribute
Validating xml against an xsd that has include and import in c#
But always end up with the same error:
System.Xml.Schema.XmlSchemaValidationException: 'The 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2:UBLExtensions' element is not declared.'
The XML that I am using, which I downloaded into a file and upload through my SWAGGER when calling the controller, is located here: https://docs.oasis-open.org/ubl/os-UBL-2.1/xml/UBL-Invoice-2.1-Example.xml
The XSD that I am using is located here: https://docs.oasis-open.org/ubl/os-UBL-2.1/xsd/maindoc/UBL-Invoice-2.1.xsd
I think you need to set settings.Schemas.XmlResolver = new XmlUrlResolver(); as well, as the flag settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation; before.
That might get you only further as I think some schemas (e.g. for signatures) are imported and not found. So in the end you will need to make sure you have local copies of those schemas and have your resolver use the local copies.

Compiling an XmlSchemaSet with imported schemas

I'm trying to create an XmlSchemaSet against the SAML 2.0 set of schema definitions, starting with the protocol schema here: https://docs.oasis-open.org/security/saml/v2.0/saml-schema-protocol-2.0.xsd
var set = new XmlSchemaSet();
XmlSchema schema;
using (var reader = XmlReader.Create(
"https://docs.oasis-open.org/security/saml/v2.0/saml-schema-protocol-2.0.xsd"))
{
schema = XmlSchema.Read(reader, (sender, e) => Console.WriteLine(e.Message));
}
set.Add(schema);
set.Compile();
When Compile is called, the following exception is thrown:
System.Xml.Schema.XmlSchemaException
Type 'urn:oasis:names:tc:SAML:2.0:assertion:EncryptedElementType' is not declared.
at System.Xml.Schema.XmlSchemaSet.InternalValidationCallback(Object sender, ValidationEventArgs e)
at System.Xml.Schema.BaseProcessor.SendValidationEvent(XmlSchemaException e, XmlSeverityType severity)
at System.Xml.Schema.BaseProcessor.SendValidationEvent(XmlSchemaException e)
at System.Xml.Schema.Compiler.CompileElement(XmlSchemaElement xe)
at System.Xml.Schema.Compiler.Compile()
at System.Xml.Schema.Compiler.Execute(XmlSchemaSet schemaSet, SchemaInfo schemaCompiledInfo)
at System.Xml.Schema.XmlSchemaSet.Compile()
at XSD.Program.Main(String[] args)
The type specified urn:oasis:names:tc:SAML:2.0:assertion:EncryptedElementType appears in the namespace imported at the top of the schema:
<import
namespace="urn:oasis:names:tc:SAML:2.0:assertion"
schemaLocation="saml-schema-assertion-2.0.xsd"/>
Using Fiddler, I can't see the application making any attempts at retrieving the imported schema.
Why don't these import statements appear to be working with the XmlSchemaSet?
The default behaviour of the XmlSchemaSet is to not try to resolve any external schemas. To this this, the XmlResolver property must be set. The go-to resolver implementation is XmlUrlResolver:
set.XmlResolver = new XmlUrlResolver();
The important thing is to set this property before adding any schemas to the set. The call to Add performs "pre-processing" on the schema, which includes resolving any import statements. Assigning the XmlResolver after calling Add appears to have no effect.
The application code needs to be:
var set = new XmlSchemaSet
{
// Enable resolving of external schemas.
XmlResolver = new XmlUrlResolver()
};
XmlSchema schema;
using (var reader = XmlReader.Create(
"https://docs.oasis-open.org/security/saml/v2.0/saml-schema-protocol-2.0.xsd"))
{
schema = XmlSchema.Read(reader, (sender, e) => Console.WriteLine(e.Message));
}
set.Add(schema);
set.Compile();
NOTE The above code still does not actually produce the desired result due to problems loading the schemas from w3.org, however the imported SAML schema is resolved successfully.

Validate an XML against XSD with imports using C#

I need to validate an XML documents against a set of XSD schemas. There is a top-level schema which imports other schemas, nested schemas may also import some schemas. For example, schema a.xsd imports b.xsd and c.xsd; b.xsd imports d.xsd. In this example a.xsd is a top-level schema.
I use the following code for a such validaton:
static void Main(string[] args)
{
try
{
var settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ConformanceLevel = ConformanceLevel.Document;
settings.Schemas.Add("targetNs", "path/to/a.xsd");
settings.ValidationEventHandler += ValidateHandler;
var reader = XmlReader.Create("path/to/file.xml", settings);
while (reader.Read()) ;
}
catch (Exception ex)
{
throw ex;
}
}
private static void ValidateHandler(object sender, ValidationEventArgs e)
{
Console.WriteLine(e.Message);
}
Although the code above implicity uses nested schemas for a valuidation, there is a problem: if some nested schema is invalid XML document it is just ignored without any exceptions.
Could you please help me with this problem?
If you need to validate the schema(s) before validating the document you can do it as follows:
try
{
using (FileStream fs = File.OpenRead("path/to/a.xsd"))
{
XmlSchema schema = XmlSchema.Read(fs, ValidateHandler);
}
}
catch (Exception e)
{
throw new Exception("Schema file is invalid. " + e.Message);
}
Then add the schema to your settings as follows:
schema.TargetNamespace = "targetNs";
settings.Schemas.Addschema(schema);

Type 'http://schemas.domain.com:Ssn' is not declared, or is not a simple type when using multiple schemas

I have two schemas, one with main definition of the object, and another one has as definiton for the types that are not simple type (string, int, decimal for example)
that is called common.xsd
When i validate, it adds both schemas but it doesn´t find the types in the included file.
Main.Xsd holds:
<xs:include schemaLocation="Common.xsd"/>
and this is the validation function:
XmlReaderSettings settings = new XmlReaderSettings();
FileStream fileStream = File.OpenRead(xsdFile);
XmlSchema schema = XmlSchema.Read(File.OpenRead(System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Schemas\\Main.xsd")), OnXsdSyntaxError);
XmlSchema schema2 = XmlSchema.Read(File.OpenRead(System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Schemas\\Common.xsd")), OnXsdSyntaxError);
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
settings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
settings.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
//þurfum að adda common.xsd líka !
settings.Schemas.Add(schema);
settings.Schemas.Add(schema2);
settings.ValidationEventHandler += new ValidationEventHandler(OnXsdSyntaxError);
using (XmlReader validator = XmlReader.Create(xmlFile, settings))
{
while (validator.Read())
isValid = true;
}
The error is:
Type 'http://schemas.domain.com:Ssn' is not declared, or is not a simple type.
So clearly it is not adding the scheme2 file ad therefor not finding definition for this custom types.
I would really appreciate help with this, because i´m forced to use multiple XSD files in this validation.
Your include will only import types from the other schema which share the same namespace as the types in your main schema.
If you want to import types from a different namespace use the namespace attribute:
<xs:include schemaLocation="Common.xsd" namespace="http://schemas.domain.com" />
Also the schemaLocation attribute is completely optional.

How do I validate an XML document against a DTD in C#?

I don't want to do anything fancy, I just want to make sure a document is valid, and print an error message if it is not. Google pointed me to this, but it seems XmlValidatingReader is obsolete (at least, that's what MonoDevelop tells me).
Edit: I'm trying Mehrdad's tip, but I'm having trouble. I think I've got most of it, but I can't find OnValidationEvent anywhere. Where go I get OnValidationEvent from?
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.DTD;
settings.ValidationEventHandler += new ValidationEventHandler(/*trouble is here*/);
XmlReader validatingReader = XmlReader.Create(fileToLoad, settings);
Instead of creating XmlValidatingReader class directly, you should construct an appropriate XmlReaderSettings object and pass it as an argument to the XmlReader.Create method:
var settings = new XmlReaderSettings { ValidationType = ValidationType.DTD };
settings.ValidationEventHandler += new ValidationEventHandler(OnValidationEvent);
var reader = XmlReader.Create("file.xml", settings);
The rest is unchanged.
P.S. OnValidationEvent is the name of the method you declare to handle validation events. Obviously, you can remove the line if you don't want to subscribe to validation events raised by the XmlReader.
var messages = new StringBuilder();
var settings = new XmlReaderSettings { ValidationType = ValidationType.DTD };
settings.ValidationEventHandler += (sender, args) => messages.AppendLine(args.Message);
var reader = XmlReader.Create("file.xml", settings);
if (messages.Length > 0)
{
// Log Validation Errors
// Throw Exception
// Etc.
}
ValidationEventHandler
Lambda Expressions
Type Inference
I've referred to this example on DTD validation.
https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlreadersettings.dtdprocessing?view=netcore-3.1#input
This example has invalid DTD XML, which I've corrected as below.
<!--XML file using a DTD-->
<!DOCTYPE store [
<!ELEMENT store (item)*>
<!ELEMENT item (name,dept,price)>
<!ATTLIST item type CDATA #REQUIRED ISBN CDATA
#REQUIRED>
<!ELEMENT name (#PCDATA)>
<!ELEMENT dept (#PCDATA)>
<!ELEMENT price (#PCDATA)>]>
<store>
<item type="supplies" ISBN="2-3631-4">
<name>paint</name>
<dept>1</dept>
<price>16.95</price>
</item>
</store>
full description:
In Visual Studio .NET, create a new Visual C# Console Application
project named ValidateXml. Add two using statements to the beginning
of Class1.cs as follows:
using System.Xml; // for XmlTextReader and XmlValidatingReader
using System.Xml.Schema; // for XmlSchemaCollection (which is used later)
In Class1.cs, declare a boolean variable named isValid before the
start of the Main method as follows:
private static bool isValid = true; // If a validation error occurs,
// set this flag to false in the
// validation event handler.
Create an XmlTextReader object to read an XML document from a text
file in the Main method, and then create an XmlValidatingReader to
validate this XML data as follows:
XmlTextReader r = new XmlTextReader("C:\\MyFolder\\ProductWithDTD.xml");
XmlValidatingReader v = new XmlValidatingReader(r);
The ValidationType property of the XmlValidatingReader object
indicates the type of validation that is required (DTD, XDR, or
Schema). Set this property to DTD as follows:
v.ValidationType = ValidationType.DTD;
If any validation errors occur, the validating reader generates a
validation event. Add the following code to register a validation
event handler (you will implement the MyValidationEventHandler
method in Step 7):
v.ValidationEventHandler +=
new ValidationEventHandler(MyValidationEventHandler);
Add the following code to read and validate the XML document. If any
validation errors occur, MyValidationEventHandler is called to
address the error. This method sets isValid to false (see Step 8).
You can check the status of isValid after validation to see if the
document is valid or invalid.
while (v.Read())
{
// Can add code here to process the content.
}
v.Close();
// Check whether the document is valid or invalid.
if (isValid)
Console.WriteLine("Document is valid");
else
Console.WriteLine("Document is invalid");
Write the MyValidationEventHandler method after the Main method as
follows:
public static void MyValidationEventHandler(object sender,
ValidationEventArgs args)
{
isValid = false;
Console.WriteLine("Validation event\n" + args.Message);
}
Build and run the application. The application should report that the XML document is valid.
e.g.:
In Visual Studio .NET, modify ProductWithDTD.xml to invalidate it (for example, delete the <AuthorName>M soliman</AuthorName> element).
Run the application again. You should receive the following error message:
Validation event
Element 'Product' has invalid content. Expected 'ProductName'.
An error occurred at file:///C:/MyFolder/ProductWithDTD.xml(4, 5).
Document is invalid

Categories

Resources