Steve H.
Steve H.

Reputation: 3373

Convert any object to a byte[]

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?

Upvotes: 162

Views: 347974

Answers (13)

alireza Jabari
alireza Jabari

Reputation: 97

using System.Text.Json;

public static class MessageEncoder
{
    public static byte[] EncodeMessage<TObject>(this TObject message)
    {
         return JsonSerializer.SerializeToUtf8Bytes(message);
    }
}

Upvotes: 1

Frank Myat Thu
Frank Myat Thu

Reputation: 4474

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

Upvotes: 13

kiran
kiran

Reputation: 1342

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 |

Upvotes: 27

Khoa Nguyen
Khoa Nguyen

Reputation: 101

Alternative way to convert object to byte array:

TypeConverter objConverter = TypeDescriptor.GetConverter(objMsg.GetType());
byte[] data = (byte[])objConverter.ConvertTo(objMsg, typeof(byte[]));

Upvotes: 1

prime_z
prime_z

Reputation: 544

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.

Upvotes: 1

Matthias Meid
Matthias Meid

Reputation: 12521

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.

Upvotes: 1

Peter Kozak
Peter Kozak

Reputation: 63

How about something simple like this?

return ((object[])value).Cast<byte>().ToArray(); 

Upvotes: 2

Error404
Error404

Reputation: 744

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

}

Upvotes: 2

Aberro
Aberro

Reputation: 629

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.

Upvotes: 36

Kevin M
Kevin M

Reputation: 5506

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

Upvotes: 112

Daniel DiPaolo
Daniel DiPaolo

Reputation: 56428

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.

Upvotes: 218

user197015
user197015

Reputation:

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.

Upvotes: 1

JaredPar
JaredPar

Reputation: 755577

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

Upvotes: 16

Related Questions