Serializing/Deserializing with Memory Stream

Serializing/deserializing with memory stream

This code works for me:

public void Run()
{
Dog myDog = new Dog();
myDog.Name= "Foo";
myDog.Color = DogColor.Brown;

System.Console.WriteLine("{0}", myDog.ToString());

MemoryStream stream = SerializeToStream(myDog);

Dog newDog = (Dog)DeserializeFromStream(stream);

System.Console.WriteLine("{0}", newDog.ToString());
}

Where the types are like this:

[Serializable]
public enum DogColor
{
Brown,
Black,
Mottled
}

[Serializable]
public class Dog
{
public String Name
{
get; set;
}

public DogColor Color
{
get;set;
}

public override String ToString()
{
return String.Format("Dog: {0}/{1}", Name, Color);
}
}

and the utility methods are:

public static MemoryStream SerializeToStream(object o)
{
MemoryStream stream = new MemoryStream();
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, o);
return stream;
}

public static object DeserializeFromStream(MemoryStream stream)
{
IFormatter formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
object o = formatter.Deserialize(stream);
return o;
}

Serializing and Deserializing object in C# without using file system

You can use Newtonsoft Jsonconvert to do this out of the box:

var stringValue = JsonConvert.SerializeObject(entity);
var object = JsonConvert.DeserializeObject<T>(stringValue);

Xml serializing and deserializing with memory stream

After serialization, the MemoryStream's position is > 0. You need to reset it before reading from it.

memStream.Position = 0;

Or...

memStream.Seek(0, SeekOrigin.Begin);

How can I deserialize a MemoryStream to an array of Structs

There is a fair amount wrong with the way are you going about it. Fundamentally, you can and should serialize and deserialize the entire collection at once. You cant step thru the memorystream item by item because you dont (cant) know the serialized size of each record. But there is more...

Use a Class not a Structure

MSDN has a good article one when and why to use a Class rather than a Structure. See Choosing Between Class and Struct

Use a List instead of an array

Arrays are grody and hard to work with because you need to now the size needed. Especially with hard coded magic numbers, if the number of students shrinks (or grows), you would not want to have to rewrite the app to change 35 everywhere.

A List(Of T) grows as needed.

Do not use GetBuffer

The internal buffer used by a MemoryStream grows by itself as needed. But it does so by doubling the buffer size each time. Which means almost half the buffer could be unused space. Use .ToArray() to get the used portion. See MemoryStream.GetBuffer Method - read the Remarks section.

But you do not even need a MemoryStream...

Use a FileStream

Rather than write to a memstream only to write it a file, you can open a filestream and write (or read) directly to that:

My Class:

<Serializable()>
Public Class Student
Public Property Name As String
Public Property Gender As String

Public Property EnrollDate As Date
Public Property FavoriteColor As String

Public Sub New()

End Sub
Public Sub New(n As String)
Name = n
End Sub

Public Overrides Function ToString() As String
Return Name & " " & EnrollDate
End Function
End Class

The ToString() override is to facilitate debugging/demo. Create a collection of Student object in a List(Of T):

Dim Students As New List(Of Student)()
Dim s As Student

s = New Student("Ziggy F")
s.EnrollDate = #5/17/2007#
s.Gender = "M"
s.FavoriteColor = "Orange"
Students.Add(s)
... etc

Console.WriteLine("BEFORE")
For Each s In Students
Console.WriteLine(s)
Next

Serialize:

Dim filename As String = "C:\Temp\myStudents.dat"

Using fs As New FileStream(filename, FileMode.Create)
Dim bf As New BinaryFormatter
bf.Serialize(fs, Students)
End Using

Deserialize and test:

Dim newStudents As List(Of Student)
' to do check if exists
Using fs As New FileStream(filename, FileMode.Open)
Dim bf As New BinaryFormatter
newStudents = DirectCast(bf.Deserialize(fs), List(Of Student))
End Using

Console.WriteLine("AFTER")
For Each s In newStudents
Console.WriteLine(s)
Next

All my students made the round trip:

BEFORE

Ziggy F 5/17/2007

Zoey P 8/1/2007

Hoover M 7/21/2005

AFTER

Ziggy F 5/17/2007

Zoey P 8/1/2007

Hoover M 7/21/2005

See also: Beginner's Guide to Classes and Lists

How to serialize object with stream in C#

After posting this I saw that this was answered already by Lasse in the comments, therefore this answer will serve as an alternative to achieving this

Here is an example which implements a custom JsonConverter that converts the File property to and from a Base64 string so it can be transferred over the network.

Some important points:

  1. You'll need to test this code in scenarios where you have a large PDF file
  2. You'll have to refactor this code to handle certain edge cases, should you identify any
  3. I have written this purely to answer your question of "can it be done", I have not considered any exceptions, edge cases or even network latency depending on how large the Base64 string will become - you will experience issues and limitations depending on the content size of the HTTP request.
  4. The API needs to know how to process this request, thus reading the Base 64 text as a stream.

Starting off, I created a StreamStringConverter

/// <summary>
/// Handles the (de)serialization of <see cref="Stream"/>.
/// </summary>
/// <remarks>
/// The <see cref="Stream"/> will be written as a Base64 encoded string, on the inverse it will be converted from a Base64 string to a <see cref="MemoryStream"/>.
/// </remarks>
public class StreamStringConverter : JsonConverter
{
private static Type AllowedType = typeof(Stream);

public override bool CanConvert(Type objectType)
=> objectType == AllowedType;

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var objectContents = (string)reader.Value;
var base64Decoded = Convert.FromBase64String(objectContents);

var memoryStream = new MemoryStream(base64Decoded);

return memoryStream;
}

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var valueStream = (FileStream)value;
var fileBytes = new byte[valueStream.Length];

valueStream.Read(fileBytes, 0, (int)valueStream.Length);

var bytesAsString = Convert.ToBase64String(fileBytes);

writer.WriteValue(bytesAsString);
}
}

You can decorate the appropriate members in your Document class to use this custom StreamStringConverter

public class Document
{
public string Number { get; set; }
public string Revision { get; set; }
public string FileName { get; set; }

// Specify a custom JsonConverter for our StreamJsonConverter
[JsonConverter(typeof(StreamStringConverter))]
public Stream File { get; set; }
}

Your model is now ready to begin serializing and deserializing, I have updated some of the code to use string in place of an actual file handle for txtFile, for simplicity.

static void Main(string[] args)
{
Document document = new Document();

const string file = "file";
const string txtFileContents = "1|1.0";
const string pdfFile = "myPdfFile.pdf";

try
{
string[] array = txtFileContents.Split('|');

FileStream fs = new FileStream(pdfFile, FileMode.Open);

document.Number = array[0];
document.Revision = array[1];
document.FileName = file;
document.File = fs;
}
catch (Exception exception)
{
}

// Serialize the Document object
// File, in the JSON contents, will be a Base64 encoded string
var serializedContents = JsonConvert.SerializeObject(document);

// Deserialize the contents
// File will be a Stream
var deserializedContents = JsonConvert.DeserializeObject<Document>(serializedContents);

// For demo purposes, this will write the Document.File object back to a new PDF file for comparison
using (var fileStream = File.Create("myDeserializedPdfFile.pdf"))
{
var fileAsMemoryStream = (MemoryStream)deserializedContents.File;
fileAsMemoryStream.WriteTo(fileStream);
}
}

Again I reiterate that I have not written this code to be production ready, that's up to you, this is simply to guide you in the right direction.

Error deserializing objects from MemoryStream

Turns out the problem was in my client's code. I was serializing it directly to the network stream instead of just converting the object to an array of bytes and sending the array.

binary formatter deserialize from memory stream freezing

You call Deserialize twice, I think problem here:

object rez = formatter.Deserialize(stream); //NEVER GOES OVER THIS
Console.WriteLine("Starting deserialization" + rez);
return formatter.Deserialize(stream);

I think you can use methods like that:

    private byte[] SerializeMessage(Message msg)
{
var formatter = new BinaryFormatter();
byte[] buf;
using (MemoryStream stream = new MemoryStream())
{
formatter.Serialize(stream, msg);
buf = new byte[stream.Length];
return stream.ToArray();
}
}

private Message DeserializeMessage(byte[] buff)
{
var formatter = new BinaryFormatter();
ConnectingMessage msg;
using (Stream stream = new MemoryStream(buff))
{
msg = formatter.Deserialize(stream) as Message;
}

return msg;
}

Also methods Send/Receive are synchronous, they block thread execution.

The description of the asynchronous option is here https://learn.microsoft.com/en-us/dotnet/framework/network-programming/using-an-asynchronous-client-socket



Related Topics



Leave a reply



Submit