19

How to serialize a rather complex structure into a byte[] array, using BinaryWriter?

Update:

  • For this to work, every structure (and sub-structure?) must be decorated with the [Serializable] attribute.

  • I do not need to implement the ISerializable interface, as this is designed to give an object control over its own serialization.

Contango
  • 76,540
  • 58
  • 260
  • 305

3 Answers3

34

Use the BinaryFormatter to serialize an object to a byte[]. BinaryWriter is just for writing bytes to a stream.

MyObject obj = new MyObject();
byte[] bytes;
IFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream())
{
   formatter.Serialize(stream, obj);
   bytes = stream.ToArray();
}
TheCodeKing
  • 19,064
  • 3
  • 47
  • 70
  • 2
    Absolutely brilliant. Just spent 2 hours on this, and you answered it in a few minutes. You are the Code King!! – Contango Sep 16 '11 at 08:54
  • 2
    @Gravitas note that `BinaryFormatter` is tightly coupled to the type model; IMO it is suitable for transport between .NET and .NET apps of the **exact** same version, but it will run into lots of problems outside that tight window. Serialization is the answer here, but there are other serializers that behave much less badly than `BinaryFormatter`. – Marc Gravell Sep 16 '11 at 08:56
  • Interesting - I'm writing this data to a file, and it needs to be portable across different versions of .NET in the future. What other serializers do you recommend to store a structure in a format we can read in the future? – Contango Sep 16 '11 at 09:05
  • @TheCodeKing - ToArray() didn't compile - is this a custom extension method? – Contango Sep 16 '11 at 09:09
  • @Gravitas in that case ***do not*** use `BinaryFormatter` - it will ***not*** work well there (and often: not at all - it doesn't exist in all .NET platforms) - I'll add an alternative – Marc Gravell Sep 16 '11 at 09:09
  • 1
    @Gravitas re `ToArray()`, change to `using (MemoryStream stream = ...` – Marc Gravell Sep 16 '11 at 09:09
  • 2
    Sorry updated, type stream to MemoryStream. – TheCodeKing Sep 16 '11 at 09:11
  • Yep if you want to make this portable to other .Net versions, there are better formats to serialize to, xml for example. – TheCodeKing Sep 16 '11 at 09:13
  • @Marc Gravell. The MemoryStream change now allows it to compile perfectly. Thanks. However, as you recommended, I'd like this file format to be portable, I will respect your recommendation on other methods. – Contango Sep 16 '11 at 09:15
  • @MarcGravell can you please give some examples of those _" lots of problems"_? – gdoron May 04 '15 at 12:45
  • 3
    @gdoron because it is tightly tied to the types (rather than an abstract schema), it has a long history of being brittle when people evolve their models - pretty much any kind of refactor (rename, move, change properties to automatically-implemented-properties, sign your assembly, change company name and thus assembly, etc) can cause massive problems. Then of course there's things like *it doesn't exist in CoreCLR* (and a range of other runtimes). I have lost count of the number of "help, I used BinaryFormatter and now I can't load my data any more" questions I've participated on. Too many. – Marc Gravell May 04 '15 at 16:48
  • If you need a fileformat to be portable, I would choose a **different** way of serialization (and possibly zip7 packed if size matters). IMO binary serialized designs are **meant to be fragile**, they also leave the smallest size. When in need of binary serializing for optimization purposes (network f.ex.), I would start by serializing a small **version marker**, which value can change in case upgrades/refactoring is necessary – Thomas Williams Nov 20 '19 at 08:20
23

From comments, the OP's scenario requires strong compatibility with future versions of the application / .NET, in which case I always advise againt BinaryFormatter - it has many "features" that simply don't work well between versions (and certainly not between platforms).

I recommend looking at contract-based serializers; I'm biased, but I lean towards protobuf-net (which maps to Google's protobuf specification). The easiest way to do this is to attribute the types in such a way that the library can make light work of them (although it can also be done without attributes), for example:

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

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

     ... etc
 }

(the attribute appoach is very familiar if you've done any XmlSerializer or DataContractSerializer work - and indeed protobuf-net can consume the attributes from those if you don't want to add protobuf-net specific attributes)

then something like:

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

The data produced this way is platform independent, and could be loaded on any matching contract (it doesn't even need to be Customer - it could any type with matching layout via the attributes). Indeed, in most cases it'll load easily into any other protobuf implementation - Java, C++, etc.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • @Gravitas - for info: http://code.google.com/p/support/issues/detail?id=5809 – Marc Gravell Sep 16 '11 at 10:53
  • 2
    By the way, I've just integrated protobuf into my code, and it works beautifully. Looking at code base, it's a truly beautiful work of art. I swear am more impressed by truly elegant code than even the best examples of skill in the Louvre. – Contango Sep 19 '11 at 10:51
  • 1
    @Gravitas you are overly generous; the internals are not particularly elegant (nor do they need to be). Library code often accepts a big chunk-o'-ugly so that the calling *application* code can be clean. See [here for more](http://programmers.stackexchange.com/questions/89620/clean-readable-code-vs-fast-hard-to-read-code-when-to-cross-the-line/89653#89653) – Marc Gravell Sep 19 '11 at 10:55
  • @Contango +1 for vain but relate-ably depressing comment – nik.shornikov Oct 01 '13 at 18:07
  • what about Version Tolerant Serialization ? https://msdn.microsoft.com/en-us/library/ms229752(v=vs.110).aspx – EKanadily Jun 03 '15 at 19:43
  • @docesam it kinda doesn't work very well - it isn't very tolerant *at all*; plus `BinaryFormatter` *simply doesn't exist* in all frameworks... – Marc Gravell Jun 03 '15 at 20:28
8

code snippet.

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);
Nuri YILMAZ
  • 4,291
  • 5
  • 37
  • 43