tags:

views:

245

answers:

5

Let's say I have two strings:

  • one is XML data
  • and the other is XSL data.

The xml and xsl data are stored in database columns, if you must know.

How can I transform the XML in C# w/o saving the xml and xsl as files first? I would like the output to be a string, too (HTML from the transformation).

It seems C# prefers to transform via files. I couldn't find a string-input overload for Load() in XslCompiledTransform. So, that's why I'm asking.

+3  A: 

You can XmlReader.Create() from a StringReader or a MemoryStream . XslCompileTransfrom can Load() from an XmlReader.

moogs
I'd use a StringReader to pass to the XmlReader.Create, actually.
John Saunders
oh yeah, that :D thanks!
moogs
A: 

I would use the XmlReader.Create(DatabaseBlobStream) and XmlWriter.Create(StringBuilder) overloads. Using the following DatabaseBlobStream object

DatabaseBlobStream.cs

internal class DatabaseBlobStream : Stream
{
    private readonly IDataReader reader;
    private readonly int columnIndex;
    private long streamPosition;

    internal DatabaseBlobStream(IDataReader reader, int columnIndex)
    {
        this.reader = reader;
        this.columnIndex = columnIndex;
    }

    public override bool CanRead
    {
        get
        {
            return reader.GetFieldType(columnIndex) == typeof (byte[])
                   && !reader.IsDBNull(columnIndex);
        }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void Flush()
    {
        throw new Exception("This stream does not support writing.");
    }

    public override long Length
    {
        get { throw new Exception("This stream does not support the Length property."); }
    }

    public override long Position
    {
        get
        {
            return streamPosition;
        }
        set
        {
            streamPosition = value;
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (reader.IsDBNull(columnIndex))
            return 0;

        int bytesRead = (int)reader.GetBytes(columnIndex, streamPosition + offset, buffer, 0, count);
        streamPosition += bytesRead;
        return bytesRead;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new Exception("This stream does not support seeking.");
    }

    public override void SetLength(long value)
    {
        throw new Exception("This stream does not support setting the Length.");
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new Exception("This stream does not support writing.");
    }

    public override void Close()
    {
        try
        {
            reader.Close();
        }
        finally
        {
            base.Close();
        }
    }

    protected override void Dispose(bool disposing)
    {
        try
        {
            reader.Dispose();
        }
        finally
        {
            base.Dispose(disposing);
        }
    }
}
bendewey
Please provide comments when downvoting, it's no help to anyone without them.
bendewey
Ben, what is the advantage of this approach over Henchman's or Moog's?
Bill Paetzke
+1  A: 

edit: using-blocks added

// input-xml
string xmlinput = String.Empty;
// xslt
string xsltinput = String.Empty;
// output-xml
string xmloutput = String.Empty;

// Prepare input-xml
XPathDocument doc = new XPathDocument(new StringReader(xmlinput));

// Prepare XSLT
XslTransform xslt = new XslTransform();
// Creates a XmlReader from your xsl string
using (XmlReader xmlreader = XmlReader.Create(new StringReader(xsltinput)))
{
    //Load the stylesheet.
    xslt.Load(xmlreader);

    // transform
    using (StringWriter sw = new StringWriter())
    {
        xslt.Transform(doc, null, sw);

        // save to string
        xmloutput = sw.ToString();
    }
}
henchman
Just downvoted. No using blocks.
John Saunders
Just using'ified. Now using blocks. Okay like that? Thanks for feedback.
henchman
+1  A: 

It took me a long time (literally years) to work out how concise code using Stream and/or TextWriter can be if you use the proper idioms.

Assuming transform and input are strings:

StringWriter sw = new StringWriter();
using (XmlReader xrt = XmlReader.Create(new StringReader(transform))
using (XmlReader xri = XmlReader.Create(new StringReader(input))
using (XmlWriter xwo = XmlWriter.Create(sw))
{
   XslCompiledTransform xslt = new XslCompiledTransform();
   xslt.Load(xrt);
   xslt.Transform(xri, xwo);
}
string output = sw.ToString();
Robert Rossney
-1: Robert, why isn't `sw` in a using block? In fact, both of the `StringReader` instances should be as well. They won't be automatically disposed when the `XmlReader` instances are.
John Saunders
Aactually the `using` blocks for the `XmlReaders` are unnecessary. The only object that it's really essential to `Close` in this scenario is the `XmlWriter`, so that any pending writes to the `StringWriter` get flushed before you get the results. Putting the `StringWriter` *inside* the `using` block would make it possible that the `XmlWriter` wouldn't be flushed and closed before you got the string's value.
Robert Rossney
+1  A: 

Here's what I went with. It's a combination of your answers. I voted up the answers that inspired this:

string output = String.Empty;
using (StringReader srt = new StringReader(xslInput)) // xslInput is a string that contains xsl
using (StringReader sri = new StringReader(xmlInput)) // xmlInput is a string that contains xml
{
    using (XmlReader xrt = XmlReader.Create(srt))
    using (XmlReader xri = XmlReader.Create(sri))
    {
        XslCompiledTransform xslt = new XslCompiledTransform();
        xslt.Load(xrt);
        using (StringWriter sw = new StringWriter())
        using (XmlWriter xwo = XmlWriter.Create(sw, xslt.OutputSettings)) // use OutputSettings of xsl, so it can be output as HTML
        {
            xslt.Transform(xri, xwo);
            output = sw.ToString();
        }
    }
}

// note: this statement is required in the xsl, in order to output as HTML:
<xsl:output method="html" omit-xml-declaration="yes" />
Bill Paetzke