C# and .NET: How to serialize a structure into a byte[] array, using BinaryWriter? [closed]
Asked Answered
B

3

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.

Bertberta answered 16/9, 2011 at 8:43 Comment(1)
protobuf-net works perfectly (see the answer below). Highly recommended.Bertberta
T
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.

Triplicate answered 16/9, 2011 at 9:17 Comment(6)
@Gravitas - for info: code.google.com/p/support/issues/detail?id=5809Triplicate
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.Bertberta
@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 moreTriplicate
@Bertberta +1 for vain but relate-ably depressing commentCalva
what about Version Tolerant Serialization ? msdn.microsoft.com/en-us/library/ms229752(v=vs.110).aspxOsana
@docesam it kinda doesn't work very well - it isn't very tolerant at all; plus BinaryFormatter simply doesn't exist in all frameworks...Triplicate
R
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();
}
Rihana answered 16/9, 2011 at 8:49 Comment(13)
Absolutely brilliant. Just spent 2 hours on this, and you answered it in a few minutes. You are the Code King!!Bertberta
@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.Triplicate
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?Bertberta
@Rihana - ToArray() didn't compile - is this a custom extension method?Bertberta
@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 alternativeTriplicate
@Gravitas re ToArray(), change to using (MemoryStream stream = ...Triplicate
Sorry updated, type stream to MemoryStream.Rihana
Yep if you want to make this portable to other .Net versions, there are better formats to serialize to, xml for example.Rihana
@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.Bertberta
@MarcGravell can you please give some examples of those " lots of problems"?Shortfall
@Shortfall 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.Triplicate
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 necessaryPathogenic
Be carefull, BinaryFormatter is now deprecated: learn.microsoft.com/en-us/dotnet/standard/serialization/…Orth
T
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.

Triplicate answered 16/9, 2011 at 9:17 Comment(6)
@Gravitas - for info: code.google.com/p/support/issues/detail?id=5809Triplicate
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.Bertberta
@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 moreTriplicate
@Bertberta +1 for vain but relate-ably depressing commentCalva
what about Version Tolerant Serialization ? msdn.microsoft.com/en-us/library/ms229752(v=vs.110).aspxOsana
@docesam it kinda doesn't work very well - it isn't very tolerant at all; plus BinaryFormatter simply doesn't exist in all frameworks...Triplicate
T
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);
Teplitz answered 25/12, 2013 at 8:59 Comment(2)
how do we deserialize?Heaver
@Jadoon updated answer, you could find how deserialize.Teplitz

© 2022 - 2024 — McMap. All rights reserved.