3

Context: .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.

4
  • 1
    I think you can't achieve your goal. XPath expressions such as //somenode will search the entire document tree and therefore require the entire document to be loaded. Commented Sep 16, 2010 at 14:20
  • 1
    If stylesheets' URIs aren't know in advance, this can't be done with pure XSLT: you can't dynamicly include or import a stylesheet. You can use template reference as in Dimitre's FXSL. Also you could staticly declare your stylesheet import chain. Otherwise, this question strictly concern .net Commented Sep 16, 2010 at 15:57
  • 1
    If you want to avoid loading the entire document in memory, you can't use XSLT. XSLT transforms aren't forward-only - if they were, you couldn't do something like <xsl:value-of select='count(//*)'/>. Commented Sep 17, 2010 at 0:57
  • Regarding your "Expected output... IXPathNavigable", see this question. Commented May 23, 2011 at 3:43

3 Answers 3

2

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)

Sign up to request clarification or add additional context in comments.

Comments

0

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.

6 Comments

+1, I don't understand why this is downvoted. The first point may go beyond the question, but still this answer is correct and very helpful.
Generally helpful perhaps, in fact I could extrapolate that xslt myself for some current work, but it doesn't answer this question at all. It demonstrates how to combine multiple xslt's into one using pure xslt, not to chain them as they are using .NET. And given that the OP's using .NET 3.5, the current state of XSLT3 while interesting, is irrelevant. The OP had specific inputs and goals, and this answer doesn't even attempt to achieve them. I can see why it was downvoted, but it wasn't me who did, I didn't see the point.
@Flynn1179: The answer clearly states that the desired goal cannot be achieved and gives a future prospect indicating that this is indeed a problem being addressed in an upcoming XSLT version. I don't see why you would downvote that (I wouldn't downvote just because the answer is 'sorry, you can't do that').
@0xA3: Thank you for your correct understanding of my answer. In it I am providing to the best of my knowledge an alternative to the requesting transformation chaining -- in XSLT, and I am explaining why this cannot be done using XSLT technology today. I am also giving a perspective with the forthcoming XSLT 3.0 streaming. As for @Flynn1179 downvoting correct answers, he had been doing this before -- at least to my answers -- and this only reflects his specific level of understanding XSLT, and also his own personality.
Oh, grow up, Dimitre. I've only ever downvoted one of your answers, and I would not have done so had it not been wrong. However, rather than correct your error, you deleted the answer to remove my comments (and the comments of the moderator who chided you for personal attacks after you called me a coward), and then reposted the same wrong answer. Since then I've stayed away from commenting/voting on your posts until today, and even here I commented that it was useful, and tried to offer an explanation of why someone might have downvoted it. You seriously need to lay off the personal attacks.
|
0

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

1 Comment

Downvoter, care to explain? My answer might not be a direct answer to the OP's problem but it shows another way of achieving the goal of a stream based transformation. STX is not a "dead" technology, it is e.g. used by several frameworks such as Cocoon, and different from the planned streaming feature in XSLT it can already be used today (though it is a different technology and more limited than XSLT).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.