Loading XSLT stylesheets embedded into an assembly - the right way

| 5 Comments | No TrackBacks

Everybody knows that XSLT stylesheet can be embedded into an assembly by setting in Visual Studio its "BuildAction" property to "Embedded Resource". Such stylesheet then can be loaded using Assembly.GetManifestResourceStream() method.

But in fact, this is actually wrong way of loading embedded stylesheets, because once smarty-pants XML developer goes and breaks XSLT stylesheet into modules it suddenly stops working - xsl:import/xsl:include are not compatible with loading stylesheet via  Assembly.GetManifestResourceStream().

The right way of loading embedded stylesheets is via XmlResolver. Having custom XmlResolver loading stylesheet from an assembly solves the problem. And even better - you can use such resolver to load main XSLT stylesheet:

using (XmlReader doc = XmlReader.Create("books.xml"))
{
  XslCompiledTransform xslt = new XslCompiledTransform();
  EmbeddedResourceResolver resolver = 
    new EmbeddedResourceResolver();
  xslt.Load("Catalog.xslt",
    XsltSettings.TrustedXslt, resolver);
  xslt.Transform(doc, XmlWriter.Create(Console.Out));
}

The EmbeddedResourceResolver class can be as simple as:

using System;
using System.Xml;
using System.Reflection;
using System.IO;

namespace EmbeddedStylesheetSample
{
  public class EmbeddedResourceResolver : XmlUrlResolver
  {        
    public override object GetEntity(Uri absoluteUri, 
      string role, Type ofObjectToReturn)
    {
      Assembly assembly = Assembly.GetExecutingAssembly();
      return assembly.GetManifestResourceStream(this.GetType(), 
      Path.GetFileName(absoluteUri.AbsolutePath));
    }
  }
}

Obviously above implementation is way too simple. Particularly it loads resources embedded into the assembly where EmbeddedStylesheetSample class is defined. This can be parametrized so e.g. the resolver can accept assembly name and optional culture name and load class from an appropriate assembly. I think I need to generalize EmbeddedResourceResolver and include it into the Mvp.Xml library so we could just use it and not reinventing again and again.

No TrackBacks

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

5 Comments

Do you know if the same can be acomplished using Saxon?

Terry, Visual Studio isn't smart enough to use any custom XmlResolvers... Really have no idea how to avoid this error messages.

Trying to implement something like this and I have the following in my Xslt file.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:resource="urn:schemas.benefittech.com:xslt">

<xsl:import href="resource:BTR.CodeGenerators.CustomToolBase, BTR.CodeGenerators:Xsl/Include/Template.xslt"/>


but VS2K5 gives me error on the stylesheet element saying: XSLT compile error. The URI prefix is not recognized.

Is there a better way to indicate this? Note, that the code actually works, it's just that I have that annoying error while editing the Xslt file.

Thanks.

Thanks for the tip, the XmlUrlResolver is the way to go _if_ your xslt is embedded.

But why oh why would anyone embed their XSLT in the assembly? The point is to separate design from logic. Even if the xslt does a non-presentational transform it _will_ be a pain to have to redeploy the assembly instead of just the stylesheet. Or not?

You should - it's been done before about a million times. Just off the top of my head (Google...):

http://www.tsbradley.net/Cookbook/Xml/xmlResourceResolver.aspx
http://msmvps.com/blogs/jayharlow/archive/2005/01/24/33766.aspx
And even my own: http://www.tomergabel.com/XML+Schema+Inclusion+In+Resources.aspx

Send me an e-mail if there's anything I can help with.

Leave a comment