How do I prepend a Stream?
Asked Answered
M

4

3

I'm loading blobs out of a database as a byte array and I put them in a memory stream so that I can load them into an xmldocument for parsing.

However there are blobs that have multiple root nodes, this causes the parser to blow up.

My solution is to just make a new root node that encompasses the whole blob.

I can add onto the end just fine with a streamwriter however I can't figure out how to add onto the beginning.

How can I prepend to a stream?


Update
I was having too much trouble getting this to work. The "XML" I was extracting was not proper XML and I kept on having to add more and more regexes to remove bad XML before the XmlDocument Load. I ended up using the HtmlAgilityPack to parse out my valid sections of XML and I put those inside their own xml documents. Not the nicest solution but it works. Sigh

Misprize answered 21/11, 2011 at 18:8 Comment(0)
B
5

Since you already have byte[] array from DB, writing more bytes before and after the array to memory stream should be easy:

// bytes from db
byte[] multipleNodes = Encoding.UTF8.GetBytes("<first>..</first><second>..</second><third>..</third>");

using (var ms = new MemoryStream())
{
    // write opening tag
    byte[] newRoot = Encoding.UTF8.GetBytes("<newRoot>");
    ms.Write(newRoot, 0, newRoot.Length);

    ms.Write(multipleNodes, 0, multipleNodes.Length);

    // write opening tag
    byte[] closeNewRoot = Encoding.UTF8.GetBytes("</newRoot>");
    ms.Write(closeNewRoot, 0, closeNewRoot.Length);

    // reset cursor position before pass it to xmldoc
    ms.Position = 0;

    var xml = new XmlDocument();
    xml.Load(ms);

    Console.WriteLine(xml.InnerXml);
}

But since XmlDocument also provide LoadXml(str), I feel manipulating the string should be more straight forward solution:

// bytes from db
byte[] multipleNodes = Encoding.UTF8.GetBytes("<first>..</first><second>..</second><third>..</third>");

string stringFromBlob = Encoding.UTF8.GetString(multipleNodes);
string withRootNode = string.Format("<newRoot>{0}</newRoot>", stringFromBlob);

var xml = new XmlDocument();
xml.LoadXml(withRootNode);

Console.WriteLine(xml.InnerXml);
Braunite answered 21/11, 2011 at 18:53 Comment(2)
Your first solution is what I wanted to suggest. Your second one, while straightforward and correct, feels too inefficient, so much so that I don't think the first one is premature optimization...Actress
@Actress not sure about this, but since the OP already got the byte[] array loaded into memory. i feel the additional cost of translating this to string (and format it along with other texts) shouldn't be very high.Braunite
T
4

You can't directly. This leads to two options:

  • write in an opening tag into the memorystream prior to loading the blobs
  • create a second memorystream, write in an opening tag, copy the first into the second...
Tradescantia answered 21/11, 2011 at 18:21 Comment(1)
Otherwise, you could treat the blob as fragments, and use this approach: #2374926Tradescantia
C
3

This is the one I use:

public class CompositeStream : FileStream
{
    Stream[] childStreams;
    int currentStreamIndex = 0;
    Stream currentStream;
    public long totalStreamRead{get; private set;}

    public CompositeStream(string pre, FileStream s_file, string post)
        : base(s_file.SafeFileHandle, FileAccess.Read)
    {
        totalStreamRead = 0;

        MemoryStream s_pre = new MemoryStream();
        MemoryStream s_post = new MemoryStream();

        byte[] b_pre = Encoding.UTF8.GetBytes(pre);
        s_pre.Write(b_pre, 0, b_pre.Length);
        s_pre.Flush();
        s_pre.Seek(0, SeekOrigin.Begin);

        byte[] b_post = Encoding.UTF8.GetBytes(post);
        s_post.Write(b_post, 0, b_post.Length);
        s_post.Flush();
        s_post.Seek(0, SeekOrigin.Begin);

        childStreams = new Stream[] { s_pre, s_file, s_post };

        currentStream = childStreams[currentStreamIndex++];
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int totalBytesRead = 0;
        while (count > 0)
        {
            // Read what we can from the current stream
            int numBytesRead = currentStream.Read(buffer, offset, count);
            totalBytesRead += numBytesRead;
            count -= numBytesRead;
            offset += numBytesRead;

            // If we haven't satisfied the read request, 
            // we have exhausted the current stream.
            // Move on to the next stream and loop around to read more data.
            if (count > 0)
            {
                // If we run out of child streams to read from...
                if (currentStreamIndex >= childStreams.Length)
                    break; //get out

                currentStream.Close();
                currentStream = childStreams[currentStreamIndex++];
            }
        }
        totalStreamRead += totalBytesRead;
        return totalBytesRead;
    }
}
Congenial answered 4/4, 2013 at 0:30 Comment(0)
A
1

A clean way to do this is to implement a CompositeStreamReader that will accept a number of streams and then read them out in order.

There is an implementation of one at https://web.archive.org/web/20100721082808/http://blogs.msdn.com/b/paolos/archive/2010/04/08/how-to-boost-message-transformations-using-the-xslcompiledtransform-class-extended.aspx that you can adapt, but you can get away with something simpler.

Anima answered 21/11, 2011 at 18:26 Comment(1)
Link is broken.Steeplebush

© 2022 - 2024 — McMap. All rights reserved.