I'm looking for language support of serialization in C#. I could derive from ISerializable and implement the serialization by copying member values in a byte buffer. However, I would prefer a more automatic way like one could do in C/C++.
Consider the following code :
using System;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace XBeeHelper
{
class XBee
{
[Serializable()]
public struct Frame<FrameType> where FrameType : struct
{
public Byte StartDelimiter;
public UInt16 Lenght;
public Byte APIIdentifier;
public FrameType FrameData;
public Byte Checksum;
}
[Serializable()]
public struct ModemStatus
{
public Byte Status;
}
public Byte[] TestSerialization()
{
Frame<ModemStatus> frame = new Frame<ModemStatus>();
frame.StartDelimiter = 1;
frame.Lenght = 2;
frame.APIIdentifier = 3;
frame.FrameData.Status = 4;
frame.Checksum = 5;
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
formatter.Serialize(stream, frame);
Byte[] buffer = stream.ToArray();
return buffer;
}
}
}
I have a generic Frame struct acting as a wrapper for many types of payload, for serial transmission. ModemStatus is an example of such payload.
However, running TestSerialization() returns a buffer 382 bytes long (without the expected content)! It should have contained 6 bytes. Is it possible to serialize this data correctly without manual serializing?
Yes, you can. I just did so with the following code. [Serializable] public struct TestStruct { public int Value1 { get; set; } public int Value2 { get; set; } public string Value3 { get; set; } public double Value4 { get; set; } } TestStruct s1 = new TestStruct(); s1. Value1 = 43265; s1.
We can use a ByteArrayOutputStream and ObjectOutputStream object to serializable an object to a byte array. Note that our User class must implement the Serializable interface.
Serialization is the process of storing the state of an object to a storage medium. In binary serialization, the public and private fields of the object and the name of the class, including the assembly containing the class, are converted to a stream of bytes, which is then written to a data stream.
Just use this two methods:
public static class StructTools
{
/// <summary>
/// converts byte[] to struct
/// </summary>
public static T RawDeserialize<T>(byte[] rawData, int position)
{
int rawsize = Marshal.SizeOf(typeof(T));
if (rawsize > rawData.Length - position)
throw new ArgumentException("Not enough data to fill struct. Array length from position: "+(rawData.Length-position) + ", Struct length: "+rawsize);
IntPtr buffer = Marshal.AllocHGlobal(rawsize);
Marshal.Copy(rawData, position, buffer, rawsize);
T retobj = (T)Marshal.PtrToStructure(buffer, typeof(T));
Marshal.FreeHGlobal(buffer);
return retobj;
}
/// <summary>
/// converts a struct to byte[]
/// </summary>
public static byte[] RawSerialize(object anything)
{
int rawSize = Marshal.SizeOf(anything);
IntPtr buffer = Marshal.AllocHGlobal(rawSize);
Marshal.StructureToPtr(anything, buffer, false);
byte[] rawDatas = new byte[rawSize];
Marshal.Copy(buffer, rawDatas, 0, rawSize);
Marshal.FreeHGlobal(buffer);
return rawDatas;
}
}
And specify your struct like this (Specify the exact size and pack (align) by one byte. default is 8):
[StructLayout(LayoutKind.Explicit, Size = 11, Pack = 1)]
private struct MyStructType
{
[FieldOffset(0)]
public UInt16 Type;
[FieldOffset(2)]
public Byte DeviceNumber;
[FieldOffset(3)]
public UInt32 TableVersion;
[FieldOffset(7)]
public UInt32 SerialNumber;
}
Now you can Deserialize using
StructTools.RawDeserialize<MyStructType>(byteArray, 0); // 0 is offset in byte[]
and serialize using
StructTools.RawSerialize(myStruct);
As Chris says, you can use unsafe code - in which case you'd better make sure you specify the layout explicitly. At that point of course you're reducing the CLR's ability to optimise a bit - you'll end up with unaligned access, loss of atomicity etc. That may well not be relevant for you, but it's worth bearing in mind.
Personally, I regard this as being a pretty fragile way to serialize/deserialize. If anything changes, your data is unreadable. If you try to run on an architecture which uses a different endianness, you'll find all your values screwed up etc. In addition, using the in-memory layout will fail as soon as you need to use an reference types - which could well influence your own design of types, encouraging you to use structs where you would otherwise use classes.
I far prefer to either explicitly read and write the values (e.g. with BinaryWriter, or preferably a version of binary writer which lets you set the endianness) or use a portable serialization framework like Protocol Buffers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With