I’m trying to migrate a large app from XslTransform to compiled xsl files and XslCompiledTransform.
The app uses the Xsl to create HTML files, and the transformation data (Xml) was passed to the Xsl with a XmlDataDocument, returned from the database.
I’ve change all that so now I do (at least temporarily):
C#
public string ProcessCompiledXsl(XmlDataDocument xml)
{
StringBuilder stringControl = new StringBuilder();
XslCompiledTransform xslTran = new XslCompiledTransform();
xslTran.Load(
System.Reflection.Assembly.Load("CompiledXsl").GetType(dllName)
);
xslTran.Transform(xml, this.Arguments, XmlWriter.Create(stringControl, othersettings), null);
return stringControl.ToString();
}
XSL (just an example)
...
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="//Object/Table">
<a href="#">
some text
</a>
</xsl:for-each>
</xsl:template>
Problem
That works, but the xsl is stripping the whitespaces between the tags outputting:
<a href="#">
some text
</a><a href="#">
some text
</a><a href="#">
some text
</a><a...etc
I’ve tried:
- Using
xml:space="preserve"but I couldn’t get it to work - Overriding the
OutputSettings, but I didn’t get any good results (maybe I missed something) - Using an
xsl:output method="xml", and that works, but creates self closing tags and a lot of other problems
So I don’t know what to do. Maybe I’m not doing something right.Any help it’s really appreciated.
Thanks!
EDIT
Just for future references, if you want to tackle this problem leaving every XSL intact, one could try this C# class I wrote, named CustomHtmlWriter.
Basically what I did is extend from XmlTextWriter and modify the methods that write the start and the end of every tag.
In this particular case, you would use it like this:
StringBuilder sb = new StringBuilder();
CustomHtmlWriter writer = new CustomHtmlWriter(sb);
xslTran.Transform(nodeReader, this.Arguments, writer);
return sb.ToString();
Hope it helps someone.
I. Solution 1:
Let me first analyze the problem here:
Given this source XML document (invented, as you haven’t provided any):
This transformation:
exactly reproduces the problem — the result is:
Now, just uncomment the commented template and comment out the first template:
The result has the wanted indentation:
And this was solution 1
II. Solution 2:
This solution may reduce to minimum the required modifications to your existing XSLT code:
This is a two-pass transformation:
The idea is that we don’t even touch the existing code, but capture its output and using a few lines of additional code only, we format the output to have the wanted, final appearance.
When this transformation is applied on the same XML document, the same, wanted result is produced:
Finally, here is a demonstration how this minor change can be introduced, without touching at all any existing XSLT code:
Let’s have this existing code in
c:\temp\delete\existing.xsl:If we run this we get the problematic output.
Now, instead of running
existing.xsl, we run this transformation:The result is the wanted one and the existing code is untouched at all:
Explanation:
We import any existing code that is at the top level of the import-precedence hierarchy (not imported by other stylesheets), using
xsl:import.We capture the output of the existing transformation in a variable. It has the infamous RTF (Result Tree Fragment) that needs to be converted to regular tree to be processed further.
The key moment is performing
xsl:apply-importswhen capturing the output of the transformation. This ensures that any template from the existing code (even one that we override — such as the template matching/) will be selected for execution as in the case when the existing transformation is performed by itself).We convert the RTF into a regular tree using the
msxsl:node-set()extension function (XslCompiledTransform also supports the EXSLTnode-set()extension function).We perform our cosmetic adjustments on the so produced regular tree.
Do Note:
This represents a general algorithm for post-processing existing transformations without touching the existing code.