views:

316

answers:

3

I have problem with serialization. I want to convert an object into a string and vice versa. I have two utility methods:

public
static byte[] Serialize(Object o)
{
   MemoryStream ms = new MemoryStream();
   BinaryFormatter bf1 = new BinaryFormatter();
   bf1.Serialize(ms, o);
   byte[] buffer = ms.ToArray();
   //string retStr = Convert.ToBase64String(buffer);
   return buffer;
}

public static object Deserialize(byte[] TheByteArray)
{
   //byte[] TheByteArray = Convert.FromBase64String(ParamStr);
   MemoryStream ms = new MemoryStream(TheByteArray);
   BinaryFormatter bf1 = new BinaryFormatter();
   ms.Position = 0;
   return bf1.Deserialize(ms);
}

My test code is:

Student obj = new Student ();
obj.UserName = "Admin";
obj.Password = "Password";
obj.lessonIds = new int[] { 1, 2, 3, 4, 5 };
obj.lessonNames= new string[] { "Spanish", "Maths" };
obj.Id= 43;
byte[] retByteArray = Crypto.Serialize(obj);

Student objNew = new Student ();
objNew = (Student )Crypto.Deserialize(retByteArray);

this code does not work. The error message is : Exception has been thrown by the target of an invocation. End of Stream encountered before parsing was completed.

End my main aim is convert object into string but I even cannot convert it into byte array

A: 

You might be suffering from a race condition, because you're not closing the memory stream or your formatter when you're done serializing.

Try this:

public
static byte[] Serialize(Object o)
{
   using (MemoryStream ms = new MemoryStream())
   {
       BinaryFormatter bf1 = new BinaryFormatter();
       bf1.Serialize(ms, o);
       byte[] buffer = ms.ToArray();
       //string retStr = Convert.ToBase64String(buffer);
   }
   return buffer;
}

public static object Deserialize(byte[] TheByteArray)
{
   //byte[] TheByteArray = Convert.FromBase64String(ParamStr);
   using (MemoryStream ms = new MemoryStream(TheByteArray))
   {
       BinaryFormatter bf1 = new BinaryFormatter();
       ms.Position = 0;
       var result = bf1.Deserialize(ms);
   }
   return result;
}
Joseph
That shouldn't make any difference in this case...
Marc Gravell
@Marc You don't think so? Why?
Joseph
You aren't doing anything extra in the code before getting the ToArray(), so you aren't changing the results. All you add is a `Dispose()` to something that has an empty `Dispose()` implementation. Don't get me wrong, I always dispose these things too - but that isn't the bug.
Marc Gravell
Oh, and BinaryFormatter isn't IDisposable...
Marc Gravell
A race condition? Why?
Trap
There isn't a race condition there. It is a good and worthwhile thought, but it doesn't stand much scrutiny.
Marc Gravell
@Marc Ok I see, but then why do both MemoryStream and BinaryFormatter have a Close? What's the point if they're not releasing resources? Should it rather be a (try/finally/Close)instead of the using?
Joseph
BinaryFormatter doesn't have a Close(). MemoryStream has one because *every* Stream has one. I fully agree that it would be better practice to use "using" here... simply that this isn't causing the bug.
Marc Gravell
@Marc Ah you're absolutely right. I misread the MSDN article in regards to BinaryFormatter. I'm going to leave my answer up anyway in case someone who finds this question can benefit from it. But thanks for your insight, it's very much appreciated!
Joseph
+1  A: 

I have just tried the original code and it work's fine however, you need to make sure that the student class definition is marked as [Serializable]

[Serializable]
public class Student
{
    public string UserName;
    public string Password;
    public int[] lessonIds;
    public string[] lessonNames;
    public int Id;
    public Student() { }
}
danrichardson
A: 

That code works perfectly for me (I simply added the missing Student class. Is that code actually representative of your real code. In particular, buffer handling (maybe file IO) would be my first suspicion. But the bug isn't in the code you posted... it works fine.

As an aside... BinaryFormatter can be brittle, especially around different versions - you may want to consider alternative serializers. Ask for more info if you are interested.

Here it is runnable:

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System;
static class Crypto
{
    static byte[] Serialize(object o)
    {
        MemoryStream ms = new MemoryStream();
        BinaryFormatter bf1 = new BinaryFormatter();
        bf1.Serialize(ms, o);
        byte[] buffer = ms.ToArray();
        //string retStr = Convert.ToBase64String(buffer);
        return buffer;
    }

    public static object Deserialize(byte[] TheByteArray)
    {
        //byte[] TheByteArray = Convert.FromBase64String(ParamStr);
        MemoryStream ms = new MemoryStream(TheByteArray);
        BinaryFormatter bf1 = new BinaryFormatter();
        ms.Position = 0;
        return bf1.Deserialize(ms);
    }
    [Serializable]
    class Student
    {
        public string UserName { get; set; }
        public string Password { get; set; }
        public int[] LessonIds { get; set; }
        public string[] LessonNames { get; set; }
        public int Id { get; set; }
    }
    static void Main()
    {
        Student obj = new Student();
        obj.UserName = "Admin";
        obj.Password = "Password";
        obj.LessonIds = new int[] { 1, 2, 3, 4, 5 };
        obj.LessonNames = new string[] { "Spanish", "Maths" };
        obj.Id = 43;
        byte[] retByteArray = Crypto.Serialize(obj);

        Student objNew = (Student)Crypto.Deserialize(retByteArray);
    }
}
Marc Gravell
For info, I jut ran it through protobuf-net for comparison: BinaryFormatter=363 bytes; protobuf-net=45 bytes...
Marc Gravell
Thank you everybody.I found my mistake. My student class implements ISerializable interface; when I remove it, it works. But I did not understand problem with interface.