Пользовательская сериализация/десериализация C# вместе с DeflateStreams

Я пытаюсь выполнить пользовательскую сериализацию/десериализацию объекта, а также сжать/распаковать сериализованные данные с помощью DeflateStreams. Первоначально я сделал это для более сложных объектов, но сократил его, чтобы попытаться выяснить проблему, однако это стало еще более загадочным, поскольку оно все еще существует. Вот класс, который нужно сериализовать/десериализовать:

[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);
        }
    }

И вот код, который должен его сериализовать (который, похоже, работает):

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();
            }

А вот и десериализация:

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();
                }

Теперь к проблеме. Десериализация создает исключение SerializationException с сообщением {"Нет карты для объекта '201326592'."} Я понятия не имею, как устранить неполадки или выяснить, что именно вызывает проблему. Очень простая сериализация работает, когда я просто запускаю методы Serialize и Deserialize BinaryFormatter в одном и том же MemoryStream.

Я попытался удалить материал DeflateStream из обоих методов, но это все та же проблема. Когда я смотрю на примеры в MSDN и других местах, кажется, что я все делаю правильно, и поиск сообщения об исключении не дает никаких значимых результатов (или, возможно, я просто плохо ищу).

PS. Как видите, я использую Miscellaneous.CopyStream(src, dest), который является базовым потоковым копировщиком, так как я не могу заставить src.CopyTo(dest) вообще работать, поэтому любые подсказки по этому поводу также приветствуются.

Ниже приведена ссылка на весь проект VS2010, если вы хотите ознакомиться с ним более внимательно: http://www.diredumplings.com/SerializationTesting.zip

ОБНОВИТЬ:

The_Smallest: я попытался использовать метод Compress, который вы разместили в моей сериализации:

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();
                    }
                }
            }

Однако, похоже, это дает мне те же проблемы, которые у меня были с srcStream.CopyTo(destStream) ранее, а именно то, что он, похоже, не записывается в поток. В результате получается файл размером 0 КБ, когда я пытаюсь сохранить его на диск. Любые идеи?

Питер: я удалил MemoryStream из метода десериализации, и, похоже, он имеет ту же функциональность, что и раньше. Однако я не уверен, как реализовать сериализацию так, как вы предложили. Это то, что вы имели в виду?

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();
            }

Спасибо вам обоим!


person vesz    schedule 28.11.2010    source источник
comment
А в чем проблема с Stream.Copy? Почему это не удается?   -  person The Smallest    schedule 28.11.2010
comment
Независимо от того, как я его использую, поток назначения всегда пуст, хотя, может быть, я просто делаю что-то глупое.   -  person vesz    schedule 28.11.2010


Ответы (3)


Я скачал ваш пример и немного покопался там. См. изменения для вашего проекта ниже:

  1. Замените LoadFromFile в 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. Замените Save в Saver.cs следующим образом:
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;
    }
}
person The Smallest    schedule 28.11.2010
comment
Это еще один ответ, потому что он специфичен для проекта @vesz, в то время как предыдущий ответ и код могут быть полезны всем, кто пытается что-то сжать или распаковать) - person The Smallest; 28.11.2010
comment
Посмотрите на это, работает как шарм. Отчаянно пытался объединить два ваших разных ответа, но ничего не получилось. Большое спасибо! - person vesz; 28.11.2010
comment
Раньше тратил много времени на проблемы со сжатием. Вот и старались помочь и сэкономить чье-то время и нервы. - person The Smallest; 28.11.2010
comment
Уже потратил почти два полных дня на эту проблему, так что нервы действительно начали трещать :) Еще раз спасибо! - person vesz; 28.11.2010

Вы должны выполнять сериализацию в DeflateStream, а не в базовый (MemoryStream) поток.

Для сериализации: начните с File.Create. Затем вокруг этого потока создайте файл DeflateStream. Затем к DefaulteStream сериализуйте свои объекты.

Для десериализации: не создавайте MemoryStream и десериализуйте непосредственно из DeflateStream.

Я считаю, что нет необходимости в добавлении MemoryStream. Однако, если у вас есть проблемы с прямой записью/чтением непосредственно из файловых потоков, просто измените процедуру сериализации, чтобы она записывала в DeflateStream вместо MemoryStream.

Это должно решить ваши проблемы.

person Pieter van Ginkel    schedule 28.11.2010
comment
Причина для MemoryStream заключалась в том, что, насколько я мог судить, я мог только читать DeflateStream, а не писать или искать в нем. Я попробую изменения выше и посмотрю, сработает ли это, спасибо. - person vesz; 28.11.2010
comment
Я разместил обновление в вопросе к вашему решению, я до сих пор не знаю, как заставить его работать. Спасибо за ваш ответ! - person vesz; 28.11.2010
comment
Замените bf.Serialize(ds.BaseStream, profile); на bf.Serialize(ds, profile);. DeflateStream — это поток, с которым вы должны общаться, чтобы получить функциональность раздувания/сдувания. Вы работаете с DeflateStream и вместо этого пишете в базовый поток. Это как если бы у вас вообще не было бы DeflateStream. Когда вы заключаете поток в DeflateStream, вы должны игнорировать исходный поток и вместо этого использовать DeflateStream. - person Pieter van Ginkel; 28.11.2010

Ошибка в логике потоков, при сжатии надо писать в CompressStream, который пишет в MemoryStream, после этого у вас будет результат в MemoryStream (а не в CompressStream) Вот пример как сжимать и распаковывать байты

    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();
        }
    }
person The Smallest    schedule 28.11.2010
comment
Я разместил обновление в вопросе к вашему решению, я до сих пор не знаю, как заставить его работать. Спасибо за ваш ответ! - person vesz; 28.11.2010