11

I have a MemoryStream object which is passed by Stream type parameter
(Stream is abstract class in C#).

I want to clone it to create another separate MemoryStream object with current position of the original and to create also a new XMLReader out of it, so I will be able to read its content.

This is what I did, and it's not working (debugging the line marked with //* -> newReader has got {None} value)
Assumption: you are inside a method and have Stream currentStream reference.

var x = new XmlReaderSettings();
x.IgnoreWhitespace = true;  

using (var newMemoryStream = new MemoryStream())
{
     stream.CopyTo(newMemoryStream);

     using (var newReader = XmlReader.Create(newMemoryStream,x)) //*
     {
                
       Doing some stuff...

     }
}
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
JavaSa
  • 5,813
  • 16
  • 71
  • 121
  • What is the value of `newMemoryStream.Position` before and after the `CopyTo` call? – Austin Salonen Dec 19 '12 at 22:06
  • @AustinSalonen: both are 0 – JavaSa Dec 19 '12 at 22:09
  • If the before and after of `Position` on `newMemoryStream` are 0, then nothing got copied into it - your source stream is at its end prior to the copy. – mlorbetske Dec 19 '12 at 22:20
  • @mlorbetske: That's can't be because until this point I was using XMLReader object to read stream's content, and I stopped in the middle to do the code I wrote above. I was able to read XML contents, now I want the same stream from the point I have stopped and new XMLReader to further read – JavaSa Dec 19 '12 at 22:24
  • 1
    Position is most likely not valid... You seem to be using some other reader on top of original `stream` - that reader could be doing block reads and position would not be exact value where you stopped reading. So you need to verify if Position is ok or force it to be correct somehow... – Alexei Levenkov Dec 19 '12 at 22:29
  • @JavaSa since XmlReader reads in buffered chunks from the source stream, where you stop reading XML isn't necessarily where you stop in the stream it's reading from. Could you check `Position` and `Length` on `stream` just prior to the `CopyTo` line? – mlorbetske Dec 19 '12 at 22:39
  • @mlorbetske: I will check as you suggested now – JavaSa Dec 19 '12 at 22:42
  • Ok, their value is 972 both before and also after `copyTo` – JavaSa Dec 19 '12 at 22:45
  • @JavaSa if by both you mean `Position` and `Length` then your stream has already been read to its end by the original `XmlReader`, you'll need to calculate the position you really want and assign it to `stream.Position` before doing the `CopyTo` – mlorbetske Dec 19 '12 at 22:48
  • yes both is `Position`and `Length`, it is weird because I can tell you exactly at which element tag I stopped before doing the above code which part of some called method's code – JavaSa Dec 19 '12 at 22:51
  • @AlexeiLevenkov: I will try to get to the same place by first creating new `XMLReader` out of new `MemoryStream` which is a copy of second already traveled stream. And then iterating new stream till the tag I stopped by in the original, doing what I need and throw this stream and reader. I guess I will be able to return to the original `XMLReader` with original stream and to continue read, because the two streams and readers are separate objects and I hold their references as well. – JavaSa Dec 19 '12 at 23:06
  • @AlexeiLevenkov: Just wanted to know ,as a developer in Microsoft why doing such a simple task is meant to be so complicated? :) – JavaSa Dec 19 '12 at 23:08
  • Having 2 readers on the same stream is not very "simple" task... Layering streams/readers is just not designed to allow such scenario: find position in lowest level stream at any particular offset of outer reader/stream. I.e. consider compression streams - reading from outer stream does not directly map to position changes of inner stream.... – Alexei Levenkov Dec 19 '12 at 23:24
  • @AlexeiLevenkov: The idea is almost as you wrote: having two readers on **two streams**, not one stream. But one stream is a copy of the other one. Hope that's not so complicated, and I will be happy if you will reference me to the `compression stream`,unfortunately I don't know this subject. – JavaSa Dec 19 '12 at 23:40
  • 1
    (Compression streams - search for GZipSteam - it wrap inner stream to compress/decompress on read/write). My understanding that you wanted second stream start from current Position in first stream. If you just need clone of whole stream with position of second one set to 0 - `new MemoryStream(((MemoryStream)stream).ToArray())` which does not seem to be interesting enough to ask a question... – Alexei Levenkov Dec 19 '12 at 23:56

3 Answers3

13

Make sure to reset the position on newMemoryStream like so:

newMemoryStream.Position = 0;

after stream.CopyTo(newMemoryStream); but before creating the XmlReader

So the whole thing should look like this:

var x = new XmlReaderSettings();
x.IgnoreWhitespace = true;  

using (var newMemoryStream = new MemoryStream())
{
    stream.CopyTo(newMemoryStream);
    newMemoryStream.Position = 0;

    using (var newReader = XmlReader.Create(newMemoryStream,x)) //*
    {
        Doing some stuff...
    }
}

Also, since you're using another reader on the original stream prior to entering this method, make sure that the Position of the source stream is really where you want it to be.

mlorbetske
  • 5,529
  • 2
  • 28
  • 40
  • Can't I copy from stream current position to newMemoryStream, I think I need that option – JavaSa Dec 19 '12 at 22:19
  • @JavaSa Copy starts at the current position of the stream `CopyTo` is called on, but (as you said in your reply on your question) the position of `newMemoryStream` is 0 after the copy. After the copy, the target stream is at its end (or at least in the position the copy finished at) - in this case indicating that nothing was copied into it, probably because the source stream was at its end (or was itself empty). – mlorbetske Dec 19 '12 at 22:25
  • what you have said is correct but two other instructions were missing , see my answer. – JavaSa Dec 20 '12 at 11:13
6

You can use the following extension method to clone a MemoryStream, including its current position, and ensure the position of the original MemoryStream is reset to its original position after cloning:

public static MemoryStream Clone(this MemoryStream ms) {
    var pos = ms.Position;
    var ms2 = new MemoryStream();
    ms.CopyTo(ms2);
    ms.Position = pos;
    ms2.Position = pos;
    return ms2;
}
Erwin Mayer
  • 18,076
  • 9
  • 88
  • 126
5

I have solved my problem! :)
The things which were missing are marked with //*. Here is the code as it should be:

var xmlReaderSettings = new XmlReaderSettings();

stream.Position = 0;//*
xmlReaderSettings.IgnoreWhitespace = true;
using (var newMemoryStream = new MemoryStream())
{
    stream.CopyTo(newMemoryStream);
    newMemoryStream.Position = 0;  //*
    using (var newReader = XmlReader.Create(newMemoryStream, xmlReaderSettings))
    {
        newReader.MoveToContent(); //*

        Doing some stuff...
    }
}
JavaSa
  • 5,813
  • 16
  • 71
  • 121