Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing to then reading from a MemoryStream

Tags:

c#

I'm using DataContractJsonSerializer, which likes to output to a Stream. I want to top-and-tail the outputs of the serializer so I was using a StreamWriter to alternately write in the extra bits I needed.

var ser = new DataContractJsonSerializer(typeof (TValue));  using (var stream = new MemoryStream()) {        using (var sw = new StreamWriter(stream))     {         sw.Write("{");          foreach (var kvp in keysAndValues)         {             sw.Write("'{0}':", kvp.Key);             ser.WriteObject(stream, kvp.Value);         }          sw.Write("}");     }      using (var streamReader = new StreamReader(stream))     {         return streamReader.ReadToEnd();     } } 

When I do this I get an ArgumentException "Stream was not readable".

I'm probably doing all sorts wrong here so all answers welcome. Thanks.

like image 609
Gaz Avatar asked Aug 05 '09 10:08

Gaz


People also ask

How do I reuse MemoryStream?

You can re-use the MemoryStream by Setting the Position to 0 and the Length to 0. By setting the length to 0 you do not clear the existing buffer, it only resets the internal counters.

Do I need to close MemoryStream?

You needn't call either Close or Dispose . MemoryStream doesn't hold any unmanaged resources, so the only resource to be reclaimed is memory. The memory will be reclaimed during garbage collection with the rest of the MemoryStream object when your code no longer references the MemoryStream .

How do you write data in a memory stream?

Write(ReadOnlySpan<Byte>) Writes the sequence of bytes contained in source into the current memory stream and advances the current position within this memory stream by the number of bytes written.

What is a MemoryStream?

MemoryStream encapsulates data stored as an unsigned byte array. The encapsulated data is directly accessible in memory. Memory streams can reduce the need for temporary buffers and files in an application. The current position of a stream is the position at which the next read or write operation takes place.


2 Answers

Three things:

  • Don't close the StreamWriter. That will close the MemoryStream. You do need to flush the writer though.
  • Reset the position of the stream before reading.
  • If you're going to write directly to the stream, you need to flush the writer first.

So:

using (var stream = new MemoryStream()) {     var sw = new StreamWriter(stream);     sw.Write("{");      foreach (var kvp in keysAndValues)     {         sw.Write("'{0}':", kvp.Key);         sw.Flush();         ser.WriteObject(stream, kvp.Value);     }         sw.Write("}");                 sw.Flush();     stream.Position = 0;      using (var streamReader = new StreamReader(stream))     {         return streamReader.ReadToEnd();     } } 

There's another simpler alternative though. All you're doing with the stream when reading is converting it into a string. You can do that more simply:

return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int) stream.Length); 

Unfortunately MemoryStream.Length will throw if the stream has been closed, so you'd probably want to call the StreamWriter constructor that doesn't close the underlying stream, or just don't close the StreamWriter.

I'm concerned by you writing directly to the the stream - what is ser? Is it an XML serializer, or a binary one? If it's binary, your model is somewhat flawed - you shouldn't mix binary and text data without being very careful about it. If it's XML, you may find that you end up with byte-order marks in the middle of your string, which could be problematic.

like image 152
Jon Skeet Avatar answered Sep 22 '22 12:09

Jon Skeet


setting the memory streams position to the beginning might help.

 stream.Position = 0;  

But the core problem is that the StreamWriter is closing your memory stream when it is closed.

Simply flushing that stream where you end the using block for it and only disposing of it fter you have read the data out of the memory stream will solve this for you.

You may also want to consider using a StringWriter instead...

using (var writer = new StringWriter()) {     using (var sw = new StreamWriter(stream))     {         sw.Write("{");          foreach (var kvp in keysAndValues)         {             sw.Write("'{0}':", kvp.Key);             ser.WriteObject(writer, kvp.Value);         }         sw.Write("}");     }      return writer.ToString(); } 

This would require your serialization WriteObject call can accept a TextWriter instead of a Stream.

like image 45
ShuggyCoUk Avatar answered Sep 21 '22 12:09

ShuggyCoUk