简体   繁体   English

C# 中结构的字节序列化字节

[英]Byte for byte serialization of a struct in C#

I'm looking for language support of serialization in C#.我正在寻找 C# 中序列化的语言支持。 I could derive from ISerializable and implement the serialization by copying member values in a byte buffer.我可以从 ISerializable 派生并通过在字节缓冲区中复制成员值来实现序列化。 However, I would prefer a more automatic way like one could do in C/C++.但是,我更喜欢一种更自动化的方式,就像在 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.我有一个通用的 Frame 结构,用作许多类型有效负载的包装器,用于串行传输。 ModemStatus is an example of such payload. ModemStatus 是此类有效负载的一个示例。

However, running TestSerialization() returns a buffer 382 bytes long (without the expected content).但是,运行 TestSerialization() 会返回一个382字节长的缓冲区(没有预期的内容)。 It should have contained 6 bytes?它应该包含 6 个字节? Is it possible to serialize this data correctly without manual serializing?是否可以在不手动序列化的情况下正确序列化这些数据?

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):并像这样指定您的结构(指定确切的大小并按一个字节打包(对齐)。默认为 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.正如Chris 所说,您可以使用不安全的代码 - 在这种情况下,您最好确保明确指定布局。 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.在这一点上,你当然会降低 CLR 的优化能力——你最终会得到不对齐的访问、原子性的丢失等。这可能与你无关,但值得牢记。

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 (eg with BinaryWriter, or preferably a version of binary writer which lets you set the endianness ) or use a portable serialization framework like Protocol Buffers .我更喜欢明确地读取和写入值(例如使用 BinaryWriter,或者最好是允许您设置字节序的二进制写入器版本)或使用可移植的序列化框架,如Protocol Buffers

See this link.请参阅此链接。 This uses the Marshal mechanism to get to the actaul data of your structs and copy them to a Byte[].这使用 Marshal 机制来获取结构的实际数据并将它们复制到 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/ http://dooba.net/2009/07/c-sharp-and-serializing-byte-arrays/

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 .原来在这里找到。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM