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?

Osprey
  • 1,523
  • 8
  • 27
  • 44
  • BTW, the `memStr.Position = 0;` in `SerializeObject` [is not required](https://learn.microsoft.com/en-us/dotnet/api/system.io.memorystream.toarray?view=net-5.0#System_IO_MemoryStream_ToArray) –  May 26 '21 at 08:51
  • Does this answer your question? [Serialize an object to string](https://stackoverflow.com/questions/2434534/serialize-an-object-to-string) – Sinatr May 26 '21 at 09:23

2 Answers2

2

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

var stringValue = JsonConvert.SerializeObject(entity);
var object = JsonConvert.DeserializeObject<T>(stringValue);
Poiter
  • 441
  • 2
  • 9
  • Works like a charm. Thanks! Two lines of code are way better than the functions I was attempting to use. – Osprey May 26 '21 at 09:14
  • Whilst answers can be of the form _['don’t do that...try this instead.](https://stackoverflow.com/help/how-to-answer)_ you have also changed the underlying format from Base64 to JSON –  May 26 '21 at 10:52
  • @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? – Dan Beaulieu Jul 14 '22 at 11:31
  • 1
    @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. –  Jul 14 '22 at 11:49
  • @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. – Dan Beaulieu Jul 14 '22 at 12:14
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.

  • 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 May 26 '21 at 09:47
  • 1
    @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 –  May 26 '21 at 10:37