我正在寻找C#中序列化的语言支持。我可以从ISerializable派生并通过在字节缓冲区中复制成员值来实现序列化。但是,我更喜欢像C / C ++那样的自动方式。
请考虑以下代码:
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;
}
}
}
我有一个通用的Frame结构,用作许多类型有效负载的包装器,用于串行传输。 ModemStatus就是这种有效载荷的一个例子。
但是,运行TestSerialization()会返回一个缓冲区 382 个字节(没有预期的内容)!它应该包含6个字节。是否可以在不手动序列化的情况下正确序列化这些数据?
答案 0 :(得分:13)
只需使用以下两种方法:
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;
}
}
并指定你的结构(指定确切的大小和打包(对齐)一个字节。默认为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;
}
现在您可以使用
进行反序列化StructTools.RawDeserialize<MyStructType>(byteArray, 0); // 0 is offset in byte[]
并使用
序列化StructTools.RawSerialize(myStruct);
答案 1 :(得分:9)
作为Chris says,您可以使用不安全的代码 - 在这种情况下,您最好确保明确指定布局。当然,你正在降低CLR优化一点的能力 - 你最终会得到不对齐的访问,失去原子性等等。这可能与你无关,但值得记住。
就我个人而言,我认为这是一种非常脆弱的序列化/反序列化方式。如果有任何变化,您的数据将无法读取。如果您尝试在使用不同字节序的架构上运行,您会发现所有值都被搞砸了。此外,只要您需要使用引用类型,使用内存中布局就会失败 - 这很可能影响你自己的类型设计,鼓励你使用你将使用类的结构。
我更喜欢明确地读取和写入值(例如使用BinaryWriter,或者最好是a version of binary writer which lets you set the endianness)或使用像Protocol Buffers这样的可移植序列化框架。
答案 2 :(得分:1)
查看此链接。这使用Marshal机制来获取结构的actaul数据并将它们复制到Byte []。另外,如何将它们复制回来。关于这些函数的优点是它们是通用的,因此它将适用于所有结构(除非它们具有像字符串一样具有可变大小的数据类型)
http://dooba.net/2009/07/c-sharp-and-serializing-byte-arrays/
答案 3 :(得分:0)
也许是通用的Serialize / Deserialize方法:
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);
}
原创here。