158

I am writing a prototype TCP connection and I am having some trouble homogenizing the data to be sent.

At the moment, I am sending nothing but strings, but in the future we want to be able to send any object.

The code is quite simple at the moment, because I thought everything could be cast into a byte array:

void SendData(object headerObject, object bodyObject)
{
  byte[] header = (byte[])headerObject;  //strings at runtime, 
  byte[] body = (byte[])bodyObject;      //invalid cast exception

  // Unable to cast object of type 'System.String' to type 'System.Byte[]'.
  ...
}

This of course is easily enough solved with a

if( state.headerObject is System.String ){...}

The problem is, if I do it that way, I need to check for EVERY type of object that can't be cast to a byte[] at runtime.

Since I do not know every object that can't be cast into a byte[] at runtime, this really isn't an option.

How does one convert any object at all into a byte array in C# .NET 4.0?

leppie
  • 115,091
  • 17
  • 196
  • 297
Steve H.
  • 3,283
  • 4
  • 24
  • 32
  • 2
    This isn't possible in any meaningful way in general (consider, for example, an instance of `FileStream`, or any object that encapsulates a handle like that). – jason Feb 01 '11 at 16:26
  • 2
    Do you intend to have all the clients running .NET? If the answer is no, you should consider some other form of serialization (XML, JSON, or the likes) – R. Martinho Fernandes Feb 01 '11 at 16:26

13 Answers13

218

Use the BinaryFormatter:

byte[] ObjectToByteArray(object obj)
{
    if(obj == null)
        return null;
    BinaryFormatter bf = new BinaryFormatter();
    using (MemoryStream ms = new MemoryStream())
    {
        bf.Serialize(ms, obj);
        return ms.ToArray();
    }
}

Note that obj and any properties/fields within obj (and so-on for all of their properties/fields) will all need to be tagged with the Serializable attribute to successfully be serialized with this.

Daniel DiPaolo
  • 55,313
  • 14
  • 116
  • 115
  • 13
    Be careful with what you do with "any" object on the other side, as it may no longer make sense (for example, if that object were a handle to a file, or similar) – Rowland Shaw Feb 01 '11 at 16:27
  • 1
    Yup, normal caveats apply, but it's not a bad idea to remind folks of them. – Daniel DiPaolo Feb 01 '11 at 16:28
  • 25
    It might be good idea to wrap the use of the MemoryStream in a `using` block, as it will eagerly release the internal buffer used. – R. Martinho Fernandes Feb 01 '11 at 16:28
  • 1
    Is this method .NET bound? Can I serialize a C struct with StructLayoutAtrribute and send via socket to a C code and expect that the C code understands the struct? I guess not? – joe Jan 18 '19 at 01:45
  • 16
    BinaryFormatter is not recommended anymore. The BinaryFormatter type is dangerous and is not recommended for data processing. see Microsoft documentation for more details. https://aka.ms/binaryformatter *The BinaryFormatter type is dangerous and is not recommended for data processing. Applications should stop using BinaryFormatter as soon as possible, even if they believe the data they're processing to be trustworthy. BinaryFormatter is insecure and can't be made secure.* – Munam Yousuf Feb 01 '21 at 15:43
  • 3
    Avoid to use BinaryFormatter due to security problems. More details here: https://learn.microsoft.com/it-it/dotnet/standard/serialization/binaryformatter-security-guide – Marco Concas Sep 24 '21 at 08:00
  • 1
    this is obsolete in .net 6 – Vinicius Bassi May 03 '22 at 20:57
112

checkout this article :http://www.morgantechspace.com/2013/08/convert-object-to-byte-array-and-vice.html

Use the below code

// Convert an object to a byte array
private byte[] ObjectToByteArray(Object obj)
{
    if(obj == null)
        return null;

    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();
    bf.Serialize(ms, obj);

    return ms.ToArray();
}

// Convert a byte array to an Object
private Object ByteArrayToObject(byte[] arrBytes)
{
    MemoryStream memStream = new MemoryStream();
    BinaryFormatter binForm = new BinaryFormatter();
    memStream.Write(arrBytes, 0, arrBytes.Length);
    memStream.Seek(0, SeekOrigin.Begin);
    Object obj = (Object) binForm.Deserialize(memStream);

    return obj;
}
Kevin M
  • 5,436
  • 4
  • 44
  • 46
  • 14
    As mentioned in a comment to [this answer](http://stackoverflow.com/a/4865123/1852105), the `MemorySteam` should be wrapped in a `using` block. – rookie1024 Jul 30 '14 at 20:33
  • is there anything I have to respect in adition? I implemented it that way and Formatting a Object containing 3 int32 public members results in a 244 Bytes long ByteArray. Am I not knowing something about C# syntax or is there anything I would probabbly miss using? – dhein Sep 25 '14 at 11:56
  • Sorry, I can't get your problem. Can you post the code? – Kevin M Sep 25 '14 at 11:59
  • @kombsh I try in short form: [Serializable] class GameConfiguration { public map_options_t enumMapIndex; public Int32 iPlayerAmount; private Int32 iGameID; } byte[] baPacket; GameConfiguration objGameConfClient = new GameConfiguration(); baPacket = BinModler.ObjectToByteArray(objGameConfClient); Now baPacket contains around 244 Bytes f content. I jsut expected 12. – dhein Sep 25 '14 at 12:13
  • 1
    @kombsh you can explicitly dispose disposable objects in your example. – Rudolf Dvoracek Sep 27 '17 at 12:30
35

Like others have said before, you could use binary serialization, but it may produce an extra bytes or be deserialized into an objects with not exactly same data. Using reflection on the other hand is quite complicated and very slow. There is an another solution that can strictly convert your objects to bytes and vise-versa - marshalling:

var size = Marshal.SizeOf(your_object);
// Both managed and unmanaged buffers required.
var bytes = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
// Copy object byte-to-byte to unmanaged memory.
Marshal.StructureToPtr(your_object, ptr, false);
// Copy data from unmanaged memory to managed buffer.
Marshal.Copy(ptr, bytes, 0, size);
// Release unmanaged memory.
Marshal.FreeHGlobal(ptr);

And to convert bytes to object:

var bytes = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, ptr, size);
var your_object = (YourType)Marshal.PtrToStructure(ptr, typeof(YourType));
Marshal.FreeHGlobal(ptr);

It's noticeably slower and partly unsafe to use this approach for small objects and structs comparing to your own serialization field by field (because of double copying from/to unmanaged memory), but it's easiest way to strictly convert object to byte[] without implementing serialization and without [Serializable] attribute.

Aberro
  • 620
  • 6
  • 10
  • 1
    Why do you think `StructureToPtr` + `Copy` is slow? How can it be slower than serialization? Is there any faster solution? – Anton Samsonov Oct 31 '13 at 17:32
  • 1
    If you use it for small structs consisting of few simple types, yes (which is quite a common case), it is slow because of marshalling and quad copying (from object to heap, from heap to bytes, from bytes to heap, from heap to object). It could be faster when IntPtr is used instead of bytes, but not in this case. And it's faster for such types to write own serializer that simply puts values into byte array. I'm not saying it's slower than build-in serialization nor that it's "so very damn slow". – Aberro Nov 26 '13 at 13:46
  • 1
    I like this method since it maps byte-by-byte. This is a really good method to exchange memory with C++ mapping. +1 for you. – Hao Nguyen Feb 08 '15 at 01:44
  • 3
    Note to potential users, while very smart, this answer doesn't work on structure arrays, objects which cannot be marshaled as an unmanaged structure or objects which have a ComVisible(false) parent in their hierarchy. – TernaryTopiary Oct 28 '16 at 05:52
  • 1
    To deserilize how you obtained the "size"? in `var bytes = new byte[size];` – Ricardo Mar 03 '19 at 12:54
  • When `Marshal.SizeOf()` throw exception: `"Type '' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed."` – Ricardo Mar 03 '19 at 13:22
  • 1
    @Ricardo, `var size = Marshal.SizeOf(your_object);`, or `var size = Marshal.SizeOf(typeof(YourType));`, I though this should be obvious. And yes, of course your type should support marshalling. You can do it with attributes: https://stackoverflow.com/questions/1345178/marshal-sizeof-error-in-computing-size – Aberro Mar 14 '19 at 11:08
24

Using Encoding.UTF8.GetBytes is faster than using MemoryStream. Here, I am using NewtonsoftJson to convert input object to JSON string and then getting bytes from JSON string.

byte[] SerializeObject(object value) =>Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value));

Benchmark for @Daniel DiPaolo's version with this version

Method                    |     Mean |     Error |    StdDev |   Median |  Gen 0 | Allocated |
--------------------------|----------|-----------|-----------|----------|--------|-----------| 
ObjectToByteArray         | 4.983 us | 0.1183 us | 0.2622 us | 4.887 us | 0.9460 |    3.9 KB |
ObjectToByteArrayWithJson | 1.548 us | 0.0309 us | 0.0690 us | 1.528 us | 0.3090 |   1.27 KB |
ns16
  • 1,322
  • 2
  • 17
  • 26
kiran
  • 1,242
  • 1
  • 10
  • 25
15

What you're looking for is serialization. There are several forms of serialization available for the .Net platform

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
13
public static class SerializerDeserializerExtensions
{
    public static byte[] Serializer(this object _object)
    {   
        byte[] bytes;
        using (var _MemoryStream = new MemoryStream())
        {
            IFormatter _BinaryFormatter = new BinaryFormatter();
            _BinaryFormatter.Serialize(_MemoryStream, _object);
            bytes = _MemoryStream.ToArray();
        }
        return bytes;
    }

    public static T Deserializer<T>(this byte[] _byteArray)
    {   
        T ReturnValue;
        using (var _MemoryStream = new MemoryStream(_byteArray))
        {
            IFormatter _BinaryFormatter = new BinaryFormatter();
            ReturnValue = (T)_BinaryFormatter.Deserialize(_MemoryStream);    
        }
        return ReturnValue;
    }
}

You can use it like below code.

DataTable _DataTable = new DataTable();
_DataTable.Columns.Add(new DataColumn("Col1"));
_DataTable.Columns.Add(new DataColumn("Col2"));
_DataTable.Columns.Add(new DataColumn("Col3"));

for (int i = 0; i < 10; i++) {
    DataRow _DataRow = _DataTable.NewRow();
    _DataRow["Col1"] = (i + 1) + "Column 1";
    _DataRow["Col2"] = (i + 1) + "Column 2";
    _DataRow["Col3"] = (i + 1) + "Column 3";
    _DataTable.Rows.Add(_DataRow);
}

byte[] ByteArrayTest =  _DataTable.Serializer();
DataTable dt = ByteArrayTest.Deserializer<DataTable>();
ns16
  • 1,322
  • 2
  • 17
  • 26
Frank Myat Thu
  • 4,448
  • 9
  • 67
  • 113
2

Combined Solutions in Extensions class:

public static class Extensions {

    public static byte[] ToByteArray(this object obj) {
        var size = Marshal.SizeOf(data);
        var bytes = new byte[size];
        var ptr = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(data, ptr, false);
        Marshal.Copy(ptr, bytes, 0, size);
        Marshal.FreeHGlobal(ptr);
        return bytes;
   }

    public static string Serialize(this object obj) {
        return JsonConvert.SerializeObject(obj);
   }

}
Error404
  • 719
  • 9
  • 30
2

How about something simple like this?

return ((object[])value).Cast<byte>().ToArray(); 
Maciej Jureczko
  • 1,560
  • 6
  • 19
  • 23
Peter Kozak
  • 63
  • 2
  • 5
1

Alternative way to convert object to byte array:

TypeConverter objConverter = TypeDescriptor.GetConverter(objMsg.GetType());
byte[] data = (byte[])objConverter.ConvertTo(objMsg, typeof(byte[]));
ns16
  • 1,322
  • 2
  • 17
  • 26
Khoa Nguyen
  • 101
  • 2
  • 3
1

One additional implementation, which uses Newtonsoft.Json binary JSON and does not require marking everything with the [Serializable] attribute. Only one drawback is that an object has to be wrapped in anonymous class, so byte array obtained with binary serialization can be different from this one.

public static byte[] ConvertToBytes(object obj)
{
    using (var ms = new MemoryStream())
    {
        using (var writer = new BsonWriter(ms))
        {
            var serializer = new JsonSerializer();
            serializer.Serialize(writer, new { Value = obj });
            return ms.ToArray();
        }
    }
}

Anonymous class is used because BSON should start with a class or array. I have not tried to deserialize byte[] back to object and not sure if it works, but have tested the speed of conversion to byte[] and it completely satisfies my needs.

ns16
  • 1,322
  • 2
  • 17
  • 26
prime_z
  • 544
  • 6
  • 9
1

I'd rather use the expression "serialization" than "casting into bytes". Serializing an object means converting it into a byte array (or XML, or something else) that can be used on the remote box to re-construct the object. In .NET, the Serializable attribute marks types whose objects can be serialized.

Matthias Meid
  • 12,455
  • 7
  • 45
  • 79
1

You could use the built-in serialization tools in the framework and serialize to a MemoryStream. This may be the most straightforward option, but might produce a larger byte[] than may be strictly necessary for your scenario.

If that is the case, you could utilize reflection to iterate over the fields and/or properties in the object to be serialized and manually write them to the MemoryStream, calling the serialization recursively if needed to serialize non-trivial types. This method is more complex and will take more time to implement, but allows you much more control over the serialized stream.

0
using System.Text.Json;

public static class MessageEncoder
{
    public static byte[] EncodeMessage<TObject>(this TObject message)
    {
         return JsonSerializer.SerializeToUtf8Bytes(message);
    }
}
eglease
  • 2,445
  • 11
  • 18
  • 28