Home » c# » xml – XSLT transform in C# – how to get valid URLs for included documents?

xml – XSLT transform in C# – how to get valid URLs for included documents?

Posted by: admin February 21, 2020 Leave a comment

Questions:

I’m transforming XML using XSLT sheet. The sheet consists of several files, which are included like this:

<xsl:include href="tokens.xsl"/>
<xsl:include href="glayout.xsl"/>
<xsl:include href="scripts.xsl"/>
<xsl:include href="tables.xsl"/>
<xsl:include href="entities.xsl"/>
<xsl:include href="cmarkup.xsl"/>

The transform code looks like following:

// Load text
var reader = XmlReader.Create(new StringReader(text));

// Load transform
XslCompiledTransform myXslTrans = new XslCompiledTransform();
using (var fs = new FileStream(result.FileName, FileMode.Open, FileAccess.Read))
{
    var xmlReader = XmlReader.Create(fs);
    myXslTrans.Load(xmlReader);
}

// Perform transformation
MemoryStream ms = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter(ms, Encoding.UTF8);
myXslTrans.Transform(reader, null, writer);

// Recover result to string
ms.Seek(0, SeekOrigin.Begin);
var textReader = new StreamReader(ms);
string transformed = textReader.ReadToEnd();

Transform fails on the include lines. I found out, that I may provide my own resolver to provide missing documents, but since their URLs are relative, I’m getting them appended to current application’s folder, like:

D:\Dokumenty\Dev\VS\Dev.Editor\Dev.Editor\bin\Debug\tokens.xsl

There are two dirty solutions:

  • Cut off the application path to retrieve only file name, then search for the file in original sheet’s folder (but what if file had a subfolder, like: Include/tokens.xsl?
  • Temporarily set current directory to the one in which main sheet resides:
    var dir = System.IO.Directory.GetCurrentDirectory();
    try
    {
        System.IO.Directory.SetCurrentDirectory(System.IO.Path.GetDirectoryName(result.FileName));
        myXslTrans.Load(xmlReader, null, resolver);
    }
    finally
    {
        System.IO.Directory.SetCurrentDirectory(dir);
    }

But I don’t like this solution either. Is there a way to force the XslCompiledTransform to pass the original URLs to the resolver? Or possibly other, more generic solution to this problem?

How to&Answers:

If you have a file name or URI with the main stylesheet module then use the overload of the Load method taking a string (https://docs.microsoft.com/en-us/dotnet/api/system.xml.xsl.xslcompiledtransform.load?view=netframework-4.8#System_Xml_Xsl_XslCompiledTransform_Load_System_String_) with e.g. myXslTrans.Load(result.FileName).