views:

2665

answers:

4

I'm looking for language support of serialization in C#. I could derive from ISerializable and implement the serialization by coying 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?

+4  A: 

The only built-in way to do this automatically involves using unsafe code to get a pointer to the underlying memory. You could theoretically write something using reflection that would bypass the unsafe code, basically iterating through all the fields on the struct and writing them to a BinaryWriter. The simplest alternative would be to just write a method that writes each value to a BinaryWriter.

Here's an article that walks through the options: Binary data from a Structure

Chris Hynes
A: 

Perhaps generic Serialize/Deserialize methods:

public static string SerializeObject<T>(T obj)
{
      string xmlString = null;
      using(MemoryStream memoryStream = new MemoryStream())
      {
     using(XmlSerializer xs = new XmlSerializer(typeof(T)))
     {
         XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
         xs.Serialize(xmlTextWriter, obj);
         memoryStream = (MemoryStream)xmlTextWriter.BaseStream;
         xmlString = UTF8ByteArrayToString(memoryStream.ToArray());      
     }
      }
      return xmlString;
}

public static T DeserializeObject<T>(string xml)
{
   XmlSerializer xs = new XmlSerializer(typeof(T));
   MemoryStream memoryStream = new MemoryStream(StringToUTF8ByteArray(xml));
   XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);
   return (T)xs.Deserialize(memoryStream);
}

Original found here.

CmdrTallen
You could have written that in half the code...
leppie
+3  A: 

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.

Jon Skeet
I am trying to define the protocol structures for a little chip accepting commands through UART (for testing). The protocol is pretty much set in stone for me, and I would never have store the data and read it back later.I would definitely follow your advice for serious ser/des though. Thanks!
joelr
+1  A: 

See this link. This uses the Marshal mechanism to get to the actaul data of your structs and copy them to a Byte[]. Also, how to copy them back. The nice thing about these functions are they are generic, so it will work with all your structs (unless they have data types that have variable sizes like strings)

http://dooba.net/2009/07/c-sharp-and-serializing-byte-arrays/