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

C# C中结构的逐字节序列化#,c#,serialization,C#,Serialization,我正在寻找C#中序列化的语言支持。我可以从ISerializable派生并通过在字节缓冲区中复制成员值来实现序列化。然而,我更喜欢一种更自动化的方式,就像在C/C++中一样 考虑以下代码: using System; using System.Text; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace XBee

我正在寻找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;
        }
    }
}
使用系统;
使用系统文本;
使用System.Runtime.Serialization;
使用System.Runtime.Serialization.Formatters.Binary;
使用System.IO;
命名空间XBeeHelper
{
XBee类
{
[可序列化()]
公共结构框架,其中FrameType:struct
{
公共字节StartDelimiter;
公共UInt16长度;
公共字节标识符;
公共框架类型框架数据;
公共字节校验和;
}
[可序列化()]
公共结构调制解调器状态
{
公共字节状态;
}
公共字节[]TestSerialization()
{
框架=新框架();
frame.StartDelimiter=1;
框架长度=2;
frame.apidentifier=3;
frame.FrameData.Status=4;
帧。校验和=5;
BinaryFormatter formatter=新的BinaryFormatter();
MemoryStream stream=新的MemoryStream();
序列化(流、帧);
字节[]缓冲区=stream.ToArray();
返回缓冲区;
}
}
}
我有一个通用的框架结构,作为许多类型的有效负载的包装器,用于串行传输。ModemStatus就是这种有效载荷的一个例子


但是,运行TestSerialization()会返回一个长度为382字节的缓冲区(没有预期的内容)!它应该包含6个字节。是否可以在不手动序列化的情况下正确序列化此数据?

可能是通用序列化/反序列化方法:

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);
}
公共静态字符串序列化对象(T obj)
{
字符串xmlString=null;
使用(MemoryStream MemoryStream=new MemoryStream())
{
使用(XmlSerializer xs=新的XmlSerializer(typeof(T)))
{
XmlTextWriter XmlTextWriter=新的XmlTextWriter(memoryStream,Encoding.UTF8);
序列化(xmlTextWriter,obj);
memoryStream=(memoryStream)xmlTextWriter.BaseStream;
xmlString=UTF8ByteArrayToString(memoryStream.ToArray());
}
}
返回xmlString;
}
公共静态T反序列化对象(字符串xml)
{
XmlSerializer xs=新的XmlSerializer(typeof(T));
MemoryStream MemoryStream=新的MemoryStream(StringToUTF8ByteArray(xml));
XmlTextWriter XmlTextWriter=新的XmlTextWriter(memoryStream,Encoding.UTF8);
返回(T)xs.反序列化(memoryStream);
}
原始版本。

因为,您可以使用不安全的代码-在这种情况下,您最好确保明确指定布局。在这一点上,您当然会降低CLR的优化能力—最终会导致访问不一致、原子性丧失等。这可能与您无关,但值得记住

就我个人而言,我认为这是一种非常脆弱的序列化/反序列化方式。如果有任何变化,您的数据将无法读取。如果你尝试在一个使用不同endianness的架构上运行,你会发现你所有的值都出错了,等等。此外,一旦你需要使用引用类型,使用内存中的布局就会失败——这很可能会影响你自己的类型设计,鼓励你在本来使用类的地方使用结构


我更喜欢显式地读取和写入值(例如,使用BinaryWriter,或者更好),或者使用可移植的序列化框架,如。

请参阅此链接。这使用封送处理机制获取结构的actaul数据,并将其复制到字节[]。还有,如何将它们复制回来。这些函数的优点是它们是泛型的,所以它可以用于所有的结构(除非它们的数据类型大小可变,比如字符串)


只需使用以下两种方法:

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;
    }
}
现在可以使用

StructTools.RawDeserialize<MyStructType>(byteArray, 0); // 0 is offset in byte[]
StructTools.RawSerialize(myStruct);

我试图为一个通过UART(用于测试)接受命令的小芯片定义协议结构。协议对我来说几乎是一成不变的,我永远不会存储数据并在以后读取回来。不过,对于严肃的ser/des,我绝对会听从你的建议。谢谢很抱歉挖掘过去,但是这个代码很糟糕。XmlSerializer不可IDisposable,因此不能在using语句中<代码>新建MemoryStream()已创建并释放,但从未使用过
memoryStream
被分配给两次,因为它是using语句的一部分,所以不会编译
UTF8ByteArrayToString()
StringToUTF8ByteArray()
只是没有在任何地方定义。也许你现在有更多的经验可以花点时间来修复它?我已经使用这个答案一个月了,它非常棒。它一定非常棒!看谁写了第二个答案。。。但乔恩仍然是StackOverflow的麦格佛。@David:链接断了
StructTools.RawSerialize(myStruct);