5
votes

I'm trying to do custom serialization/deserialization of an object as well as compressing/decompressing the serialized data with DeflateStreams. I originally did this for more complex objects but cut it down to try and figure out the problem, however it just became more puzzling as it is still there. Here is the class to be serialized/deserialized:

[Serializable]
    public class RandomObject : ISerializable
    {
        public String Name { get; set; }
        public String SavePath { get; set; }

        public RandomObject()
        {
        }

        public RandomObject(String name, String savepath)
        {
            Name = name;
            SavePath = savepath;
        }

        public RandomObject(SerializationInfo info, StreamingContext context)
            : this(info.GetString("name"), info.GetString("savepath"))
        {
        }
        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("name", Name);
            info.AddValue("savepath", SavePath);
        }
    }

And here is the code that is supposed to serialize it(which seems to work):

BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize(ms, profile);
                using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress))
                {
                    try
                    {
                        using (FileStream fs = File.Create(path))
                        {
                            ds.Flush();
                            Miscellaneous.CopyStream(ds.BaseStream, fs);
                            fs.Flush();
                            fs.Close();
                        }
                    }
                    catch (IOException e)
                    {
                        MessageBox.Show(e.Message);
                        success = false;
                    }
                    ds.Close();
                }
                ms.Close();
            }

And here is the deserialization:

RandomObject profile = null;
                using (FileStream fs = File.OpenRead(path))
                {
                    using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Decompress))
                    {
                        BinaryFormatter bf = new BinaryFormatter();
                        ds.Flush();

                        using (MemoryStream ms = new MemoryStream())
                        {
                            Miscellaneous.CopyStream(ds.BaseStream, ms);
                            profile = bf.Deserialize(ms) as RandomObject;
                            profile.SavePath = path;
                            ms.Close();
                        }
                        ds.Close();
                    }
                    fs.Close();
                }

Now, to the problem. Deserialization throws a SerializationException with the message {"No map for object '201326592'."} I have no idea how to troubleshoot or figure out what exactly is causing the problem. Very basic serialization works when I just run BinaryFormatter's Serialize and Deserialize methods on the same MemoryStream.

I tried removing the DeflateStream stuff from both methods, but it's still the same problem. When I look at the examples at MSDN and other places it looks like I'm doing it just right, and googling for the exception message doesn't give any meaningful results(or perhaps I'm just bad at searching).

PS. As you can see I use Miscellaneous.CopyStream(src, dest) which is a basic stream copier, as I can't get src.CopyTo(dest) to work at all, so any hints on that is welcome as well.

Below is a link to the whole VS2010 project if you would like to look at it more closely: http://www.diredumplings.com/SerializationTesting.zip

UPDATE:

The_Smallest: I tried using the Compress method you posted on my serialization:

BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream stream = new MemoryStream())
            {
                bf.Serialize(stream, profile);

                byte[] array = Compress(stream);

                using (MemoryStream ms = new MemoryStream(array))
                {
                    using (FileStream fs = File.Create(path))
                    {
                        ms.WriteTo(fs);
                        fs.Flush();
                    }
                }
            }

However, it seems to give me the same problems that I had with srcStream.CopyTo(destStream) earlier, which is that it doesn't seem to get written to the stream. The result is a 0 kb file when I try to save it to disk. Any ideas?

Pieter: I removed the MemoryStream from the deserialization method and it seems have the same functionality as before. However I'm not sure how to implement the serialization the way you suggested. Is this what you had in mind?

BinaryFormatter bf = new BinaryFormatter();

            using (FileStream fs = File.Create(path))
            {
                using (DeflateStream ds = new DeflateStream(fs, CompressionMode.Compress))
                {
                    bf.Serialize(ds, profile);
                    fs.Flush();
                    ds.Close();
                }
                fs.Close();
            }

Thanks to both of you!

3
And what is the problem with Stream.Copy? Why does it fail?The Smallest
No matter how I use it the destination stream is always empty, might just be me doing something stupid though.vesz

3 Answers

1
votes

You should serialize into the DeflateStream, not the base (MemoryStream) stream.

For serializing: begin with the File.Create. Then around that stream, create the DeflateStream. Then to the DefaulteStream, serialize your objects.

For deserializing: do not create the MemoryStream and deserialize directly from the DeflateStream.

I believe there is no need for the added MemoryStream. If however you do have problems writing directly to/reading directly from the file streams, just change the serialize routine to write to the DeflateStream instead of the MemoryStream.

That should solve your issues.

1
votes

There is error in streams logics, while compressing you should write to CompressStream, which writes to MemoryStream, after this you will have result in MemoryStream (not in CompressStream) Here is example how to compress and decompress bytes

    private static byte[] Compress(Stream stream)
    {
        using (var resultStream = new MemoryStream())
        {
            using (var gzipStream = new DeflateStream(resultStream, CompressionMode.Compress))
                stream.CopyTo(gzipStream);
            return resultStream.ToArray();
        }
    }

    private static byte[] Decompress(byte[] bytes)
    {
        using (var readStream = new MemoryStream(bytes))
        using (var resultStream = new MemoryStream())
        {
            using (var gzipStream = new DeflateStream(readStream, CompressionMode.Decompress))
                gzipStream.CopyTo(resultStream);
            return resultStream.ToArray();
        }
    }
1
votes

I dowloaded you example and digged a little in there. See changes for your project below:

  1. Replace LoadFromFile in Loader.cs
private static RandomObject LoadFromFile(string path)
{
  try
  {
    var bf = new BinaryFormatter();
    using (var fileStream = File.OpenRead(path))
    using (var decompressed = new MemoryStream())
    {
      using (var deflateStream = new DeflateStream(fileStream, CompressionMode.Decompress))
        deflateStream.CopyTo(decompressed);

      decompressed.Seek(0, SeekOrigin.Begin);
      var profile = (RandomObject)bf.Deserialize(decompressed);
      profile.SavePath = path;
      return profile;
    }
  }
  catch (IOException e)
  {
    MessageBox.Show(e.Message);
    return null;
  }
}

  1. Replace Save in Saver.cs as follows:
public static bool Save(RandomObject profile, String path)
{
   try
   {
      var bf = new BinaryFormatter();
      using (var uncompressed = new MemoryStream())
      using (var fileStream = File.Create(path))
      {
         bf.Serialize(uncompressed, profile);
         uncompressed.Seek(0, SeekOrigin.Begin);

         using (var deflateStream = new DeflateStream(fileStream, CompressionMode.Compress))
           uncompressed.CopyTo(deflateStream);
      }
      return true;
    }
    catch (IOException e)
    {
       MessageBox.Show(e.Message);
       return false;
    }
}