C# и .NET: как сериализовать структуру в массив byte[] с помощью BinaryWriter?

Как сериализовать довольно сложную структуру в массив byte[] с помощью BinaryWriter?

Обновление:

  • Чтобы это работало, каждая структура (и подструктура?) должна быть украшена атрибутом [Serializable].

  • Мне не нужно реализовывать интерфейс ISerializable, так как он предназначен для предоставления объекту контроля над его собственной сериализацией.


person Contango    schedule 16.09.2011    source источник
comment
protobuf-net работает отлично (см. ответ ниже). Настоятельно рекомендуется.   -  person Contango    schedule 19.09.2011


Ответы (3)


Судя по комментариям, сценарий ОП требует сильной совместимости с будущими версиями приложения/.NET, и в этом случае я всегда советую против BinaryFormatter — у него много «фич», которые просто плохо работают между версиями (и уж точно не между платформами).

Я рекомендую смотреть сериализаторы на основе контрактов; Я предвзят, но склоняюсь к protobuf-net (что соответствует спецификации protobuf от Google). Самый простой способ сделать это — атрибутировать типы таким образом, чтобы библиотека могла легко с ними работать (хотя это можно сделать и без атрибутов), например:

 [ProtoContract]
 public class Customer {
     [ProtoMember(1)]
     public List<Order> Orders {get {....}}

     [ProtoMember(2)]
     public string Name {get;set;}

     ... etc
 }

(подход к атрибуту очень знаком, если вы выполняли какую-либо работу с XmlSerializer или DataContractSerializer - и действительно, protobuf-net может использовать атрибуты из них, если вы не хотите добавлять специфические атрибуты protobuf-net)

затем что-то вроде:

Customer cust = ...
byte[] data;
using(var ms = new MemoryStream()) {
    Serializer.Serialize(ms, cust);
    data = ms.ToArray();
}

Данные, созданные таким образом, не зависят от платформы и могут быть загружены в любой соответствующий контракт (они даже не должны быть Customer — они могут быть любого типа с соответствующим макетом через атрибуты). Действительно, в большинстве случаев он легко загружается в любую другую реализацию protobuf — Java, C++ и т. д.

person Marc Gravell    schedule 16.09.2011
comment
@Gravitas – для информации: code.google.com/p/support/ проблемы/подробности?id=5809 - person Marc Gravell; 16.09.2011
comment
Кстати, я только что интегрировал protobuf в свой код, и он прекрасно работает. Глядя на кодовую базу, это действительно прекрасное произведение искусства. Клянусь, я больше впечатлен по-настоящему элегантным кодом, чем даже лучшими образцами мастерства в Лувре. - person Contango; 19.09.2011
comment
@Gravitas, ты слишком щедр; внутренности не особенно элегантны (да и не должны быть). Библиотечный код часто принимает большие куски безобразия, поэтому вызывающий код приложения может быть чистым. См. подробнее здесь - person Marc Gravell; 19.09.2011
comment
@Contango +1 за тщетный, но удручающий комментарий - person nik.shornikov; 01.10.2013
comment
как насчет устойчивой к версии сериализации? msdn.microsoft.com/en-us/library /ms229752(v=vs.110).aspx - person EKanadily; 03.06.2015
comment
@docesam это работает не очень хорошо - это не очень терпимо совсем; плюс BinaryFormatter просто не существует во всех фреймворках... - person Marc Gravell; 03.06.2015

Используйте BinaryFormatter для сериализации объекта в байт. []. BinaryWriter предназначен только для записи байтов в поток.

MyObject obj = new MyObject();
byte[] bytes;
IFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
   formatter.Serialize(stream, obj);
   bytes = stream.ToArray();
}
person TheCodeKing    schedule 16.09.2011
comment
Абсолютно блестящий. Просто потратил на это 2 часа, а вы ответили на него через несколько минут. Ты Король Кода!! - person Contango; 16.09.2011
comment
@Gravitas отмечает, что BinaryFormatter тесно связан с моделью типов; IMO подходит для переноса между приложениями .NET и .NET точно одной и той же версии, но за пределами этого узкого окна возникнет множество проблем. Здесь ответом является сериализация, но есть и другие сериализаторы, которые ведут себя гораздо менее плохо, чем BinaryFormatter. - person Marc Gravell; 16.09.2011
comment
Интересно — я записываю эти данные в файл, и в будущем их нужно будет переносить между разными версиями .NET. Какие еще сериализаторы вы рекомендуете для хранения структуры в формате, который мы сможем читать в будущем? - person Contango; 16.09.2011
comment
@TheCodeKing - ToArray() не скомпилировался - это пользовательский метод расширения? - person Contango; 16.09.2011
comment
@Gravitas в этом случае не использует BinaryFormatter - там он не хорошо работает (и часто: не на all - не на всех платформах .NET есть) - добавлю альтернативу - person Marc Gravell; 16.09.2011
comment
@Gravitas re ToArray(), измените на using (MemoryStream stream = ... - person Marc Gravell; 16.09.2011
comment
Извините, обновлено, введите поток в MemoryStream. - person TheCodeKing; 16.09.2011
comment
Да, если вы хотите сделать это переносимым для других версий .Net, есть лучшие форматы для сериализации, например, xml. - person TheCodeKing; 16.09.2011
comment
@Марк Гравелл. Изменение MemoryStream теперь позволяет ему отлично компилироваться. Спасибо. Однако, как вы рекомендовали, я бы хотел, чтобы этот формат файла был переносимым, я буду уважать вашу рекомендацию по другим методам. - person Contango; 16.09.2011
comment
@MarcGravell, не могли бы вы привести несколько примеров этих много проблем? - person gdoron is supporting Monica; 04.05.2015
comment
@gdoron, потому что он тесно связан с типами (а не с абстрактной схемой), он имеет долгую историю хрупкости, когда люди развивают свои модели - практически любой вид рефакторинга (переименование, перемещение, изменение свойств на автоматически реализуемые- свойства, подписать свою сборку, изменить название компании и, следовательно, сборку и т. д.) могут вызвать серьезные проблемы. Затем, конечно, есть такие вещи, как это не существует в CoreCLR (и ряде других сред выполнения). Я потерял счет помощи, я использовал BinaryFormatter, и теперь я не могу больше загружать свои данные по вопросам, в которых я участвовал. Очень много. - person Marc Gravell; 04.05.2015
comment
Если вам нужен переносимый формат файла, я бы выбрал другой способ сериализации (и, возможно, zip7, если размер имеет значение). Двоичные сериализованные конструкции IMO должны быть хрупкими, они также оставляют наименьший размер. Когда мне нужна двоичная сериализация для целей оптимизации (например, сети), я бы начал с сериализации небольшого маркера версии, значение которого может измениться в случае необходимости обновления/рефакторинга. - person Thomas Williams; 20.11.2019

фрагмент кода.

public static byte[] XmlSerializeToByte<T>(T value) where T : class
{
    if (value == null)
    {
        throw new ArgumentNullException();
    }

    XmlSerializer serializer = new XmlSerializer(typeof(T));

    using (MemoryStream memoryStream = new MemoryStream())
    {
        using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream))
        {
            serializer.Serialize(xmlWriter, value);

            return memoryStream.ToArray();
        }
    }
}

    public static T XmlDeserializeFromBytes<T> (byte[] bytes)
                                     where T : class
    {
        if (bytes == null || bytes.Length == 0)
        {
            throw new InvalidOperationException();
        }

        XmlSerializer serializer = new XmlSerializer(typeof(T));

        using (MemoryStream memoryStream = new MemoryStream(bytes))
        {
            using (XmlReader xmlReader = XmlReader.Create(memoryStream))
            {
                return (T)serializer.Deserialize(xmlReader);
            }
        }
    }


        //Serialize
        Duck duck = new Duck() { Name = "Donald Duck" };
        byte[] bytes = Test.XmlSerializeToByte(duck);
        //Deserialize
        var deDuck = Test.XmlDeserializeFromBytes<Duck>(bytes);
        Console.WriteLine(deDuck.Name);
person Nuri YILMAZ    schedule 25.12.2013
comment
как мы десериализуем? - person Haseeb Jadoon; 03.07.2015
comment
@Jadoon обновил ответ, вы можете найти, как десериализовать. - person Nuri YILMAZ; 03.07.2015