Scott Hanselman

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. 

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Tuesday, January 25, 2005 6:22:27 AM UTC
hi

could you post the xsd's too
i have an xsd sample and this does not seem to work.
it can never for some reason find my xmlurlResolver
-siddharth
siddharth
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.