在 .NET5 之前,我们通过这些代码序列化/反序列化字节/对象:
private static byte[] StructToBytes<T>(T t)
{
using (var ms = new MemoryStream())
{
var bf = new BinaryFormatter();
bf.Serialize(ms, t);
return ms.ToArray();
}
}
private static T BytesToStruct<T>(byte[] bytes)
{
using (var memStream = new MemoryStream())
{
var binForm = new BinaryFormatter();
memStream.Write(bytes, 0, bytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
var obj = binForm.Deserialize(memStream);
return (T)obj;
}
}
但是出于安全原因,BinaryFormatter 将被删除:
https://learn.microsoft.com/en-us/dotnet/standard/serialization/binaryformatter-security-guide
那么有没有一些简单但高性能的方法来替代 BinaryFormatter?
在我最近从 .NET Core 3.1 迁移到 .NET 5 的项目中,我用 Protobuf-net 替换了 BinarySerializer 代码:https://github.com/protobuf-net/protobuf-net
代码几乎完全相同,该项目在 GitHub 上(目前)有 2200 万次下载和 3200 颗星,享有很高的声誉。它速度非常快,并且没有 BinarySerializer 周围的任何安全包袱。
这是我的 byte[] 序列化课程:
public static class Binary
{
/// <summary>
/// Convert an object to a Byte Array, using Protobuf.
/// </summary>
public static byte[] ObjectToByteArray(object obj)
{
if (obj == null)
return null;
using var stream = new MemoryStream();
Serializer.Serialize(stream, obj);
return stream.ToArray();
}
/// <summary>
/// Convert a byte array to an Object of T, using Protobuf.
/// </summary>
public static T ByteArrayToObject<T>(byte[] arrBytes)
{
using var stream = new MemoryStream();
// Ensure that our stream is at the beginning.
stream.Write(arrBytes, 0, arrBytes.Length);
stream.Seek(0, SeekOrigin.Begin);
return Serializer.Deserialize<T>(stream);
}
}
我确实必须向我序列化的类添加属性。它只用 [Serializable] 装饰,虽然我知道 Protobuf 可以使用很多常见的装饰,但那个没有用。来自 github 上的示例:
[ProtoContract]
class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
[ProtoMember(3)]
public Address Address {get;set;}
}
[ProtoContract]
class Address {
[ProtoMember(1)]
public string Line1 {get;set;}
[ProtoMember(2)]
public string Line2 {get;set;}
}
就我而言,我在 Redis 中缓存内容,效果很好。
也可以在您的 .csproject 文件中重新启用它:
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
...但这是个坏主意。 BinaryFormatter 是许多 .NET 历史漏洞的罪魁祸首,而且无法修复。它可能在未来的 .NET 版本中变得完全不可用,因此替换它是正确的做法。
如果您使用的是 .NET Core 5 或更高版本,则可以像这样使用新的
System.Text.Json.JsonSerializer.Serialize
和 System.Text.Json.JsonSerializer.Deserialize
:
public static class Binary
{
/// <summary>
/// Convert an object to a Byte Array.
/// </summary>
public static byte[] ObjectToByteArray(object objData)
{
if (objData == null)
return default;
return Encoding.UTF8.GetBytes(JsonSerializer.Serialize(objData, GetJsonSerializerOptions()));
}
/// <summary>
/// Convert a byte array to an Object of T.
/// </summary>
public static T ByteArrayToObject<T>(byte[] byteArray)
{
if (byteArray == null || !byteArray.Any())
return default;
return JsonSerializer.Deserialize<T>(byteArray, GetJsonSerializerOptions());
}
private static JsonSerializerOptions GetJsonSerializerOptions()
{
return new JsonSerializerOptions()
{
PropertyNamingPolicy = null,
WriteIndented = true,
AllowTrailingCommas = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
}
}
虽然是旧线程,但它仍然相关,尤其是如果您发现自己处理在 Memcached(或 Redis,或本地或云中的二级存储)中存储 .NET 数据的代码。
BinaryFormatter
有OP中提到的安全问题,也有性能和尺寸问题。
一个很好的选择是 MessagePack 格式,更具体地说是 .NET 解决方案的 MessagePack NuGet 包。
它是安全的、维护的、更快的、更小的。有关详细信息,请参阅基准。
ZeroFormatter 似乎也是一个不错的选择。
在当今以云为中心的解决方案中,规模和容量对于降低成本非常重要,这些非常有用。
在 .NET Core 5 中有一个使用它的选项:
只需添加
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
像这样的项目:
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
我相信它会起作用。