Validating that XmlSchemas and their Imports are Valid and All Good with an XmlResolver. July 15, '04 Comments [1] Posted in XML | Bugs Sponsored By The very awesome Oleg Tkachenko commented in a recent post of mine (as did Patrick Cauldwell, in person) that what I was doing could have been accomplished with a custom XmlResolver. Both are absolutely right.I did my little hack because it was quick but Oleg's right, it would have been "more correct" to do something like this:public class XmlCustomResolver : XmlUrlResolver{ override public object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) { //Here, mess with absoluteUri.AbsolutePath and return an XPathNavigator or Stream or whatever }}So what happens is that you pass in the Resolver into the call to .Compile like:foreach(XmlSchema x in w.Schemas){ x.Compile(new ValidationEventHandler(OnValidationEvent), new XmlBaseDirectoryResolver());}However, my problem was a smidge more subtle than it initially appeared. The problem was, for me, that I want to resolve the schemaLocations (which look like "banking/someDomainObject.xsd") relative to the path that the WSDL is in, like "C:\dev\whatever\wsdl\". However, by the time we get into the Resolver (when .Compile calls back to GetEntity()) the propery absoluteUrl.AbsolutePath already contains "C:\dev\MyTestConsole\bin\debug\someDomain\banking\someDomainObject.xsd." See? It's already "pre-resolved" the path relative to AppDomain.CurrentDomain.CurrentDirectory. At this point, I have no way (that I can see) to know what was the original relative path. I want the schemaLocation to be "C:\dev\whatever\wsdl\banking\someDomainObject.xsd." I could have passed the directory of the WSDL file into the constructor call to the Resolver. So we add:foreach(XmlSchema x in w.Schemas){ x.Compile(new ValidationEventHandler(OnValidationEvent), new XmlBaseDirectoryResolver(wsdlFile.DirectoryName));}Note that the AbsolutePath has the Directory Separators as "/", so I have to fix those as well. Here's the final "XmlBaseDirectoryResolver." It's more lines of code, but it's also reusable.public class XmlBaseDirectoryResolver : XmlUrlResolver{ private string baseDir = String.Empty; public XmlBaseDirectoryResolver(string baseDirectory) : base() { baseDir = baseDirectory; } override public object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn) { if (absoluteUri.IsFile == true) { //Change the directory characters to the same ones that AppDomain.CurrentDomain.BaseDirectory uses string newFileName = absoluteUri.AbsolutePath.Replace('/',Path.DirectorySeparatorChar); //Now, yank the automatically added portion... newFileName = newFileName.Replace(AppDomain.CurrentDomain.BaseDirectory,String.Empty); //Add our Base Directory... newFileName = Path.Combine(baseDir, newFileName); //Return the file... return new FileStream(newFileName, FileMode.Open, FileAccess.Read, FileShare.Read); } return base.GetEntity(absoluteUri, role, ofObjectToReturn); }}The big problem with this particular result? It doesn't work with relative paths that use the dotdotslash "../../whatever/this.xsd." At this point - the point of resolution - too much has been already resolved for me. :) The only way I could fix that would be to work backwards to figure out how many ../..'s were removed for me, and put them back. Not worth it.Oleg has a great article up on his blog on how to Create your Own XmlResolver. His actually retrieves the schema (stored) in a SQL Server. « Using the Server (rather than Workstatio... | Blog Home | The Computer Back - Pain and the Program... » About Scott Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author. About Newsletter Sponsored By Hosting By