C#中结构的字节序列化字节
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/628843/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Byte for byte serialization of a struct in C#
提问by joelr
I'm looking for language support of serialization in C#. I could derive from ISerializable and implement the serialization by copying member values in a byte buffer. However, I would prefer a more automatic way like one could do in C/C++.
我正在寻找 C# 中序列化的语言支持。我可以从 ISerializable 派生并通过复制字节缓冲区中的成员值来实现序列化。但是,我更喜欢一种更自动的方式,就像在 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. ModemStatus is an example of such payload.
我有一个通用的 Frame 结构,它充当多种类型的有效负载的包装器,用于串行传输。ModemStatus 是此类有效负载的一个示例。
However, running TestSerialization() returns a buffer 382bytes long (without the expected content)! It should have contained 6 bytes. Is it possible to serialize this data correctly without manual serializing?
但是,运行 TestSerialization() 会返回一个382字节长的缓冲区(没有预期的内容)!它应该包含 6 个字节。是否可以在不手动序列化的情况下正确序列化这些数据?
回答by CmdrTallen
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.
原来在这里找到。
回答by Jon Skeet
As Chris says, you can use unsafe code - in which case you'd better make sure you specify the layout explicitly. 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.
正如Chris 所说,您可以使用不安全的代码 - 在这种情况下,您最好确保明确指定布局。到那时,您当然会稍微降低 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 (e.g. 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这样的可移植序列化框架。
回答by Jon Skeet
See this link. This uses the Marshal mechanism to get to the actaul data of your structs and copy them to a 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)
请参阅此链接。这使用 Marshal 机制获取结构的实际数据并将它们复制到 Byte[]。另外,如何将它们复制回来。这些函数的好处是它们是通用的,因此它可以与您的所有结构一起使用(除非它们具有可变大小的数据类型,如字符串)
http://dooba.net/2009/07/c-sharp-and-serializing-byte-arrays/
http://dooba.net/2009/07/c-sharp-and-serializing-byte-arrays/
回答by JCH2k
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);