我有一组需要写入二进制文件的对象。
我需要文件中的字节是紧凑的,所以我不能使用BinaryFormatter
。 BinaryFormatter
会为反序列化需求提供各种信息。
如果我尝试
byte[] myBytes = (byte[]) myObject
我收到运行时异常。
我需要这么快,所以我宁愿不要复制字节数组。我只想让演员byte[] myBytes = (byte[]) myObject
工作!
好的只是要清楚,我不能在输出文件中有任何元数据。只是对象字节。打包的对象到对象。根据收到的答案,看起来我将编写低级Buffer.BlockCopy
代码。也许使用不安全的代码。
答案 0 :(得分:134)
将对象转换为字节数组:
// Convert an object to a byte array
public static byte[] ObjectToByteArray(Object obj)
{
BinaryFormatter bf = new BinaryFormatter();
using (var ms = new MemoryStream())
{
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
您只需要将此函数复制到您的代码中,然后将需要转换为字节数组的对象发送给它。如果需要再次将字节数组转换为对象,可以使用以下函数:
// Convert a byte array to an Object
public static Object ByteArrayToObject(byte[] arrBytes)
{
using (var memStream = new MemoryStream())
{
var binForm = new BinaryFormatter();
memStream.Write(arrBytes, 0, arrBytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
var obj = binForm.Deserialize(memStream);
return obj;
}
}
您可以将这些功能与自定义类一起使用。您只需在类中添加[Serializable]
属性即可启用序列化
答案 1 :(得分:34)
如果您希望序列化数据非常紧凑,您可以自己编写序列化方法。这样你就可以减少开销。
示例:
public class MyClass {
public int Id { get; set; }
public string Name { get; set; }
public byte[] Serialize() {
using (MemoryStream m = new MemoryStream()) {
using (BinaryWriter writer = new BinaryWriter(m)) {
writer.Write(Id);
writer.Write(Name);
}
return m.ToArray();
}
}
public static MyClass Desserialize(byte[] data) {
MyClass result = new MyClass();
using (MemoryStream m = new MemoryStream(data)) {
using (BinaryReader reader = new BinaryReader(m)) {
result.Id = reader.ReadInt32();
result.Name = reader.ReadString();
}
}
return result;
}
}
答案 2 :(得分:28)
从myObject
到byte[]
的演员阵容永远不会有效,除非你有明确的转换或myObject
a {{1 }}。您需要某种类型的序列化框架。那里有很多,包括Protocol Buffers,这对我来说很亲近。就空间和时间而言,它非常“精益和平均”。
你会发现几乎所有的序列化框架都对你可以序列化的内容有很大的限制,但是 - 由于是跨平台的,协议缓冲比一些更多。
如果你能提出更多要求,我们可以为你提供更多帮助 - 但它永远不会像铸造一样简单......
编辑:只是回应这个:
我需要我的二进制文件来包含 对象的字节。只有字节,没有 元数据无论如何。袋装 对象到对象。所以我会的 实现自定义序列化。
请记住,对象中的字节经常是引用...所以你需要弄清楚如何处理它们。
我怀疑你会发现设计和实现自己的自定义序列化框架比你想象的要难。
我个人建议,如果您只需要针对几种特定类型执行此操作,那么您就不必费心去尝试一般的序列化框架。只需在所需的所有类型中实现实例方法和静态方法:
byte[]
要记住的一件事:如果你有继承权,一切都变得更加棘手。没有继承,如果您知道自己的类型,则不需要包含任何类型信息。当然,还有版本控制的问题 - 您是否需要担心不同版本的类型的向后和向前兼容性?
答案 3 :(得分:12)
你真的在谈论序列化,它可以采取多种形式。由于您需要小型和二进制,协议缓冲区可能是一个可行的选择 - 同时提供版本容错和可移植性。与BinaryFormatter
不同,协议缓冲区有线格式不包括所有类型元数据;只是非常简洁的标记来识别数据。
在.NET中有一些实现;特别是
我谦虚地认为protobuf-net(我写的)允许更多的.NET惯用法与典型的C#类(“常规”协议缓冲区倾向于需求代码生成);例如:
[ProtoContract]
public class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
}
....
Person person = new Person { Id = 123, Name = "abc" };
Serializer.Serialize(destStream, person);
...
Person anotherPerson = Serializer.Deserialize<Person>(sourceStream);
答案 4 :(得分:11)
我接受了Crystalonics的回答并将其转换为扩展方法。我希望其他人会发现它们很有用:
public static byte[] SerializeToByteArray(this object obj)
{
if (obj == null)
{
return null;
}
var bf = new BinaryFormatter();
using (var ms = new MemoryStream())
{
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
public static T Deserialize<T>(this byte[] byteArray) where T : class
{
if (byteArray == null)
{
return null;
}
using (var memStream = new MemoryStream())
{
var binForm = new BinaryFormatter();
memStream.Write(byteArray, 0, byteArray.Length);
memStream.Seek(0, SeekOrigin.Begin);
var obj = (T)binForm.Deserialize(memStream);
return obj;
}
}
答案 5 :(得分:2)
查看Serialization,一种将整个对象“转换”为字节流的技术。您可以将其发送到网络或将其写入文件,然后将其还原回对象。
答案 6 :(得分:2)
这对我有用:
byte[] bfoo = (byte[])foo;
foo
是一个对象,我100%肯定是一个字节数组。
答案 7 :(得分:1)
要直接访问对象的内存(进行“核心转储”),您需要进入不安全的代码。
如果你想要比BinaryWriter更紧凑或者原始内存转储给你的东西,那么你需要编写一些自定义序列化代码,从对象中提取关键信息并以最佳方式打包它。
修改强> 附:将BinaryWriter方法包装到DeflateStream中以压缩数据非常容易,这通常会使数据大小减半。
答案 8 :(得分:1)
我找到了另一种在byte []中转换对象的方法。希尔是我的解决方案:
IEnumerable en = (IEnumerable) myObject;
byte[] myBytes = en.OfType<byte>().ToArray();
此致
答案 9 :(得分:1)
我相信你要做的事情是不可能的
在程序停止后,BinaryFormatter
创建的垃圾是从文件中恢复对象所必需的
然而,有可能获得对象数据,您只需要知道它的确切大小(比听起来更困难):
public static unsafe byte[] Binarize(object obj, int size)
{
var r = new byte[size];
var rf = __makeref(obj);
var a = **(IntPtr**)(&rf);
Marshal.Copy(a, r, 0, size);
return res;
}
这可以通过以下方式恢复:
public unsafe static dynamic ToObject(byte[] bytes)
{
var rf = __makeref(bytes);
**(int**)(&rf) += 8;
return GCHandle.Alloc(bytes).Target;
}
上述方法不适用于序列化的原因是返回数据中的前四个字节对应于RuntimeTypeHandle
。 RuntimeTypeHandle
描述了对象的布局/类型,但每次运行程序时它的值都会改变
编辑:这是愚蠢的不这样做 - &gt;
如果您已经知道要反序列化的对象的类型,则可以在反序列化时切换 BitConvertes.GetBytes((int)typeof(yourtype).TypeHandle.Value)
的那些字节。
答案 10 :(得分:0)
如果您只有文字或类似商店的内容,您可以执行以下操作:
byte[] byteArray = Encoding.ASCII.GetBytes(myObject.text);
否则,您将不得不以更复杂的方式序列化。
答案 11 :(得分:0)
使用二进制格式化程序现在被认为是不安全的。见 --> Docs Microsoft
只需使用 System.Text.Json:
要序列化为字节:
<块引用>JsonSerializer.SerializeToUtf8Bytes(obj);
反序列化为您的类型:
<块引用>JsonSerializer.Deserialize(byteArray);