我正在寻找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 Framewhere 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 frame = new Frame (); 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个字节.是否可以在不手动序列化的情况下正确序列化此数据?
只需使用以下两种方法:
public static class StructTools { ////// converts byte[] to struct /// public static T RawDeserialize(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; } /// /// converts a struct to byte[] /// 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(byteArray, 0); // 0 is offset in byte[]
和序列化使用
StructTools.RawSerialize(myStruct);
正如Chris所说,您可以使用不安全的代码 - 在这种情况下,您最好确保明确指定布局.当然,你正在降低CLR优化一点的能力 - 你最终会得到不对齐的访问,失去原子性等等.这可能与你无关,但值得记住.
就个人而言,我认为这是一个非常脆弱的序列化/反序列化方式.如果有任何变化,您的数据将无法读取.如果您尝试在使用不同字节序的体系结构上运行,您将发现所有值都被搞砸了.此外,只要您需要使用引用类型,使用内存中布局就会失败 - 这很可能影响你自己的类型设计,鼓励你使用结构,否则你会使用类.
我更喜欢显式读取和写入值(例如,使用BinaryWriter,或者最好使用允许您设置字节序的二进制写入器版本)或使用协议缓冲区等可移植序列化框架.