1

I am attempting to save an object as serialized string and then I need to deserialize it back to an object at a later stage. I want to avoid using the file system.

The serializing is being done with this function:

  public string SerializeObject<T>(T objectToSerialize)
  {
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream memStr = new MemoryStream();
    try
    {
      bf.Serialize(memStr, objectToSerialize);
      memStr.Position = 0;
      return Convert.ToBase64String(memStr.ToArray());
    }
    finally
    {
      memStr.Close();
    }
  }

Which I got from here.

This appears to be working. However, when I attempt to deserialize to an object using the following functions, I am getting an error.

  public Call deSerialize(string Serialized)
  {
    Stream Fs = GenerateStreamFromString(Serialized);
    BinaryFormatter F = new BinaryFormatter();
    Call s1 = (Call)F.Deserialize(Fs);
    Fs.Close();
    return s1;
  }

  public Stream GenerateStreamFromString(string s)
  {
    MemoryStream stream = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);
    writer.Write(Convert.FromBase64String(s));
    writer.Flush();
    stream.Position = 0;
    return stream;
  }

The error I am getting is:

End of Stream encountered before parsing was completed.

And it is occurring on the line reading:

Call s1 = (Call)F.Deserialize(Fs);

Is there a way around this, or perhaps a better way to deserialize a string without using the file system?

2
  • BTW, the memStr.Position = 0; in SerializeObject<T> is not required Commented May 26, 2021 at 8:51
  • Does this answer your question? Serialize an object to string Commented May 26, 2021 at 9:23

2 Answers 2

2

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

var stringValue = JsonConvert.SerializeObject(entity);
var object = JsonConvert.DeserializeObject<T>(stringValue);
Sign up to request clarification or add additional context in comments.

5 Comments

Works like a charm. Thanks! Two lines of code are way better than the functions I was attempting to use.
Whilst answers can be of the form 'don’t do that...try this instead. you have also changed the underlying format from Base64 to JSON
@MickyD I don't believe the OP said there was a requirement to keep it in base64. If the OP was your team-mate and came to your desk and asked how to serialize and deserialize a class,... and then when on about BinaryFormatters, - don't you think you'd stop them and point them towards json serialization?
@DanBeaulieu so the question was predominantly about why does my deserialisation of base64 payload fail? not how can I rewrite my code entirely because it has a bug and I can't fix it? The problem with the latter and demonstrated with the above answer is that the original problem is never solved and so the programmer doesn't get a chance to learn and grow. Programmers need to learn how code works, how to debug and not download that fashionable NuGet package where the thinking has already been done for them.
@MickyD Actually the OP's question never mentioned base64... anywhere in his actual question. That said, I'm not invalidating your answer, though I think one could argue that you've missed the "opportunity" to teach a new programmer to use the correct tool for the job. I'm simply commenting that this is a perfectly reasonable answer, despite your objection above.
2

Disclaimer: I don't have anything against the use of Newtonsoft Json in general


The problem is that you are using StreamWriter to write the re-constructed bytes from Convert.FromBase64String to a MemoryStream.

MSDN: (my empasis)

Implements a TextWriter for writing characters to a stream in a particular encoding.

In your case this results in a much smaller buffer in MemoryStream thus leading to an exception later in Deserialize.

We can see the differences in sizes below:

MemStreamTest.exe Information: 0 : Position: 206 after save
MemStreamTest.exe Information: 0 : ToArray returned: 206 bytes
MemStreamTest.exe Information: 0 : Position: 13 after reconstruction

Change this:

public Stream GenerateStreamFromString(string s)
  {
    MemoryStream stream = new MemoryStream();
    StreamWriter writer = new StreamWriter(stream);
    writer.Write(Convert.FromBase64String(s));
    writer.Flush();
    stream.Position = 0;
    return stream;
  }

...to:

public Stream GenerateStreamFromString(string s)
{
    MemoryStream stream = new MemoryStream();
    var bytes = Convert.FromBase64String(s);
    stream.Write(bytes, 0, bytes.Length);
    Trace.TraceInformation($"Position: {stream.Position} after reconstruction");
    stream.Position = 0;
    return stream;
}


The following example of Call results in 206 bytes in each MemoryStream.

var call = new Call {PhoneNumber = "0812345678", Duration = TimeSpan.FromMinutes(5)};

[Serializable]
public class Call
{
    public TimeSpan Duration { get; set; }
    public string PhoneNumber { get; set; }
}

Results:

MemStreamTest.exe Information: 0 : Position: 206 after save
MemStreamTest.exe Information: 0 : ToArray returned: 206 bytes
MemStreamTest.exe Information: 0 : Position: 206 after reconstruction

Complete listing

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace MemStreamTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var program = new Program();
            program.Run();
        }

        private void Run()
        {
            var call = new Call {PhoneNumber = "0812345678", Duration = TimeSpan.FromMinutes(5)};
            var contents = SerializeObject(call);

            var other = deSerialize(contents);
        }

        public string SerializeObject<T>(T objectToSerialize)
        {
            BinaryFormatter bf = new BinaryFormatter();
            MemoryStream memStr = new MemoryStream();
            try
            {
                bf.Serialize(memStr, objectToSerialize); // serialise as binary
                Trace.TraceInformation($"Position: {memStr.Position} after save");
                memStr.Position = 0;
                var bytes = memStr.ToArray();
                Trace.TraceInformation($"ToArray returned: {bytes.Length} bytes");
                
                return Convert.ToBase64String(bytes); // binary to Base64
            }
            finally
            {
                memStr.Close();
            }
        }

        public Call deSerialize(string Serialized)
        {
            Stream Fs = GenerateStreamFromString(Serialized);
            BinaryFormatter F = new BinaryFormatter();
            Call s1 = (Call)F.Deserialize(Fs);
            Fs.Close();
            return s1;
        }

        public Stream GenerateStreamFromString(string s)
        {
            MemoryStream stream = new MemoryStream();
            var bytes = Convert.FromBase64String(s);
            stream.Write(bytes, 0, bytes.Length);
            Trace.TraceInformation($"Position: {stream.Position} after reconstruction");
            stream.Position = 0;
            return stream;
        }
    }

    [Serializable]
    public class Call
    {
        public TimeSpan Duration { get; set; }
        public string PhoneNumber { get; set; }
    }
}

Final thoughts

Unless you really want a Base64 payload you would be advised to use Newtonsoft Json.

2 Comments

Thanks @MickyD for the elaborate answer and for actually explaining what was wrong with my approach. I really appreciate it. However, Poiter 's solution does what I needed in two lines of code.
@Osprey not a problem. I would recommend NewtonsoftJson too. However it's always good to know what we were doing wrong else we never learn. In this case that StreamWriter is for text not binary

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.