簡體   English   中英

C# 中結構的字節序列化字節

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

我正在尋找 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 個字節? 是否可以在不手動序列化的情況下正確序列化這些數據?

只需使用這兩種方法:

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);

正如Chris 所說,您可以使用不安全的代碼 - 在這種情況下,您最好確保明確指定布局。 在這一點上,你當然會降低 CLR 的優化能力——你最終會得到不對齊的訪問、原子性的丟失等。這可能與你無關,但值得牢記。

就個人而言,我認為這是一種非常脆弱的序列化/反序列化方式。 如果有任何變化,您的數據將無法讀取。 如果您嘗試在使用不同字節序的架構上運行,您會發現所有值都搞砸了等等。此外,一旦您需要使用引用類型,使用內存布局就會失敗——這很可能影響您自己的類型設計,鼓勵您在原本會使用類的地方使用結構。

我更喜歡明確地讀取和寫入值(例如使用 BinaryWriter,或者最好是允許您設置字節序的二進制寫入器版本)或使用可移植的序列化框架,如Protocol Buffers

請參閱此鏈接。 這使用 Marshal 機制來獲取結構的實際數據並將它們復制到 Byte[]。 另外,如何將它們復制回來。 這些函數的好處是它們是通用的,因此它將適用於您的所有結構(除非它們具有可變大小的數據類型,如字符串)

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

也許通用的序列化/反序列化方法:

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);
}

原來在這里找到。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM