I love XmlResolvers

| 3 Comments | No TrackBacks

Did you know XslTransform class allows custom XmlResolver to return not only Stream (it's only what default XmlResolver implementation - XmlUrlResolver class supports), but also XPathNavigator! Sounds like undeservedly undocumented feature. What it gives us? Really efficient advanced XML resolving scenarios such as just mentioned recently on asp.net XML forum - getting access to XML fragments from within XSLT. Or looking up for cached in-memory XML documents. Or constructing XML documents on the fly for XSLT, e.g. via accessing SQL Server database from within XSLT stylesheet and processing the result. Well, part of it could be done also with XSLT parameters and extension functions, but XmlResolver is more powerful, flexible and elegant approach.

Here is a sample XmlFragmentResolver, which allows XSLT to get access to external XML fragments (XML fragment aka external general parsed entity is well-formed XML with more than one root elements):

public class XmlFragmentResolver : XmlUrlResolver
{
  override public object GetEntity(Uri absoluteUri, string role, 
    Type ofObjectToReturn)
  {    
    using (FileStream fs = File.OpenRead(absoluteUri.AbsolutePath))
    {
      XmlTextReader r = new XmlTextReader(fs, 
          XmlNodeType.Element, null);
      XPathDocument doc = new XPathDocument(r);
      return doc.CreateNavigator();
    }
  }
}
Don't forget to pass its instance to Transform() method (in .NET 1.0 - set it to XslTransform.XmlResolver property):
xslt.Transform(doc, null, Console.Out, new XmlFragmentResolver());
And here is how then you can access XML fragments from within XSLT:
<xsl:apply-templates select="document('d:/temp/fragment.xml')/*"/>

Note, that instead you can load XML fragment and pass it as a parameter, but then you should know statically in advance all XML fragments/documents XSLT would ever require. XmlResolver approach allows XSLT to take over and access external documents or fragments really dynamically, e.g. when a file name cannot be known prior to the transformation.

Related Blog Posts

No TrackBacks

TrackBack URL: http://www.tkachenko.com/cgi-bin/mt-tb.cgi/156

3 Comments

It looks ok. Only tabs get disappeared, right? That might be MovableType fault too.

btw, you can just return Stream from GetEntity, but I'm not sure whether it's any better.

Mmm.... RssBandit "Post Reply" feature is not preserving linebreaks and spaces :S. Sorry Oleg!

What's more, I often use an EmbeddedResourceResolver which goes something like this:

override public object GetEntity(Uri uri, string role, Type ret)
{
if (uri.Scheme == "asm")
{
using (Stream stm = Assembly.GetExecutingAssembly().GetManifestResourceStream(uri.Host))
{
XPathDocument doc = new XPathDocument(stm);
return doc;
}
}
else
{
return base.GetEntity(uri, role, ret);
}
}


This is yet another use, which I often take advantage of to resolve embedded WXS files with included/imported schemas (which are also embedded, of course ;)).
In the XSLT, the uri would look:

select="document('asm://NMatrix.Xml.Schematron.xsd')/*"/>