Complex XSDs as Embedded Resources

I was recently tasked with validating XML files against a very complex set of XSD schema files.  This is easily accomplished if your XSD files live on the filesystem, because the .Net xml resolver can find referenced schemas via a relative or absolute Uri.  In my case, the schema files were compiled as embedded resources in my project.  As expected, the XML schema loader didn’t know how to find referenced schemas – it was likely searching for them  in the path of the running application.  To solve this, I had to help out the schema loader by implementing a custom resolver that knew how to pull schema from the embedded resources.  The code…

Implement a custom XmlUrlResolver to find schema references as embedded resources.

public class ManifestResourceResolver : XmlUrlResolver
{
    private readonly Assembly _resourceAssembly;
    private readonly string _baseNamespaceForReferences;

    public ManifestResourceResolver(Assembly resourceAssembly, string baseNamespaceForReferences)
    {
        _resourceAssembly = resourceAssembly;
        _baseNamespaceForReferences = baseNamespaceForReferences.EndsWith(".") ? baseNamespaceForReferences : baseNamespaceForReferences + ".";
    }

    override public object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
    {
        if (absoluteUri.IsFile)
        {
            var file = Path.GetFileName(absoluteUri.AbsolutePath);
            var stream = _resourceAssembly.GetManifestResourceStream(_baseNamespaceForReferences + file);
            return stream;
        }
        return null;
    }
}

Create a XmlSchemaSet, and set the XmlResolver property to the custom resolver implemented above. Then, load up the main XSD schema.

var xmlSchemaSet = new XmlSchemaSet();
xmlSchemaSet.XmlResolver = new ManifestResourceResolver(Assembly.GetExecutingAssembly(), "MyApp.Schemas");
using (var stream = Assembly.GetCallingAssembly().GetManifestResourceStream("MyApp.Schemas.MainSchema.xsd"))
{
    var xmlSchema = XmlSchema.Read(stream, null);
    xmlSchemaSet.Add(xmlSchema);
}

Create a XmlReaderSettings object and set the Schemas property to the schema set loaded above. Lastly, load up an XML file with an XmlReader and read it.

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

XmlReader reader = XmlReader.Create("SomeXml.xml", settings);
while (reader.Read());

The validation callback function will be invoked on each validation error while reading the file with the XmlReader.

private static void ValidationCallBack(object sender, ValidationEventArgs e)
{
    Console.WriteLine("Validation Error: {0}", e.Message);
}

References: