How to chain/nest XSL Transforms in .NET
Co开发者_Python百科ntext: .NET Framework 3.5
I realize how I can perform a single XML transformation using XSLT, but didn't find any good examples on chaining XML transformations.
Input: - XML document as XPathDocument. - File paths to multiple XSL files.
Expected output: - preferably XPathDocument/IXPathNavigable, representing the XML with all transformations applied, one by one.
Example scenario:
input xml: <doc></doc>
xsl-1: .xsl that adds <one />
as a child of the doc element.
xsl-2: .xsl that adds <two />
as a child of the doc element.
Expected result
<doc><one /><two /></doc>
Goals
Leverage the forward only nature of XPathDocument/IXPathNavigable or better. Avoid loading entire document in memory.
Maybe something like the following (I have not tried to compile this):
XslCompiledTransform xsl1 = new XslCompiledTransform();
xsl1.Load("xsl1.xsl");
XslCompiledTransform xsl2 = new XslCompiledTransform();
xsl1.Load("xsl2.xsl");
using (Stream stream = new MemoryStream())
{
using (XmlReader xmlReader1 = XmlReader.Create("source.xml"))
{
xsl1.Transform(xmlReader1, stream);
}
stream1.Position = 0;
using (XmlReader xmlReader2 = XmlReader.Create(stream))
{
xsl2.Transform(xmlReader2, "output.xml");
}
}
By using the xmlreader you will get the forward only you are looking for. I have just outpu the first result to a MemoryStream but you could do this to a temporary file.
For that extra little bit of performance you may want to look at pre compiling your xslt.
XSLT Compiler (xsltc.exe)
I. This demonstrates how to perform multiple-pass XSLT 1.0 transformations with any XSLT 1.0 processor that supports the exslt node-set() extension function, including .NET XslCompiledTransform.
For other XSLT 1.0 processors one needs to replace ext:node-set()
by whatever they support, such as msxsl:node-set()
(with msxsl associated with the correct namespace) for MSXML.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="ext xsl">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="node()"/>
</xsl:variable>
<xsl:apply-templates mode="pass2"
select="ext:node-set($vrtfPass1)/node()"/>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="@*"/>
<one/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|@*" mode="pass2">
<xsl:copy>
<xsl:apply-templates select="node()|@*" mode="pass2"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*/one" mode="pass2" >
<xsl:call-template name="identity"/>
<two/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on this XML document:
<doc/>
the wanted result is produced:
<doc>
<one/>
<two/>
</doc>
II. At present XSLT 1.0 and XSLT 2.0 use XPath and this requires the whole XML document (representation) to be in RAM.
The Working Draft for XSLT 2.1 (recently renamed to 3.0) proposes a new, streaming feature, which, if this WD becomes an official recommendation, will make it possible for compliant XSLT 3.0 processors to implement streaming.
III. XslCompiledTransform of an XPathDocument
still holds the whole XML document representation in memory. The fact that an XPathDocument
provides forward-only iteration doesn't mean that XslCompiledTransform performs any streaming of the XML document.
If you want to avoid loading the entire document tree into memory you might want to consider a different technology than XSLT.
There is an approach called Streaming Transformations for XML (STX), which does not require the construction of an in-memory tree. You might want to check out the STX project on SourceForge.
For an overview on streaming transformations I'd recommend the following article:
An Introduction to Streaming Transformations for XML
精彩评论