views:

198

answers:

1

Hello,

I want to read an xml file, apply a transform, then write to another file. The best way I can think of is this:

using (XmlTextReader reader = new XmlTextReader(new FileStream(_source, FileMode.Open)))
using (XmlTextWriter writer = new XmlTextWriter(new StreamWriter(_destination)))
{
   _xslTransform.Transform(reader, writer);
}

But I really want to get a progress. If I'm just copying data I can do something like this (this might not be 100% right but something like this):

using (BinaryReader reader = new BinaryReader(new FileStream(_source, FileMode.Open)))
using (BinaryWriter writer = new StreamWriter(_destination))
{
    byte[] buffer = new byte[2048];
    int read = 0;
    int actual = 0;
    long total = reader.BaseStream.Length;

    while ((read = reader.Read(buffer, 0, buffer.Length)) > 0)
    {
        writer.Write(buffer, 0, read);
        actual = (actual <= total ? actual + read : read);
        updateProgress(Convert.ToInt32(actual / total * 100));
    }
}

So is there some way of doing this when using the XslCompiledTransform. I could just read it into memory first before writing to the file but I want to limit the amount of memory used, or does the XslCompiledTransform load it all into memory anyway?

I hope this makes some sort of sense, thanks!

Adam.

+2  A: 

Hi Adam,

An option is to send xsl:message messages from within the transform and listen to these messages.

With this approach you have to run over the input document first and count the nodes that your transform "iterates" over. This can be done using a very basic XSLT which just sends a message on every node of interest.

Then - during the actual transform - each time you hit a node of interest you can send an

<xsl:message terminate="no">progress:mynode</xsl:message>

progress message.

This message can be intercepted in the calling C# code with a message listener. You could put the following code into a small class for applying an XSL transform. It's just a basic sample for illustrating; I hope you get the idea:

protected event MessageListener progressMessageIntercepted;

public void AddProgressMessageListener(MessageListener listener)
{
    progressMessageIntercepted += listener;
}

public void RemoveMessageListeners()
{
    progressMessageIntercepted = null;
}

protected void MessageCallBack(object sender, XsltMessageEncounteredEventArgs e)
{
    if (e.Message.StartsWith("progress:"))
    {
        if (progressMessageIntercepted != null)
        {
            progressMessageIntercepted(this, null);
        }
    }
}

protected void Transform(string inputFile, string outputFile, string xsltFile)
{
    XslCompiledTransform xslt = new XslCompiledTransform();
    xslt.Load(xsltFile);
    XsltArgumentList parameters = new XsltArgumentList();
    parameters.XsltMessageEncountered += new XsltMessageEncounteredEventHandler(MessageCallBack);

    using (XmlWriter xmlWriter = XmlWriter.Create(outputFile))
    {
        xslt.Transform(inputFile, parameters, xmlWriter);
    }
}
0xA3
I'm not going to downvote it because it's a clever answer and I don't have a better one, but I have to say I'm not sure I like putting coding logic like that into a xsl file, which is meant to describe how to format/transform an XML document. It's not good separation of concerns.
Neil Barnwell
Disagree. You don't have to put coding logic into the XSLT, you only have to add "trace" messages at very few points, e.g. the beginning of one match-template.
0xA3
(continued) How else would you be able to inform a client about progress and where you are in your transformation without inserting additional "logic" into your XSLT?
0xA3