高级API (MessagePackSerializer)
MessagePackSerializer类是MessagePack for C# 的入口点。静态方法构成了MessagePack for C# 的主要API。
API | Description |
---|---|
Serialize<T> |
将一个对象图序列化为MessagePack二进制块。可以使用异步变体获取Stream。也有非泛型重载可用。 |
Deserialize<T> |
将MessagePack二进制转换为对象图。可用于Stream的异步变体。也有非泛型重载可用。 |
SerializeToJson |
将MessagePack兼容的对象图序列化为JSON,而不是MessagePack。这对调试很有帮助。 |
ConvertToJson |
将MessagePack二进制转换为JSON。这对调试很有帮助 |
ConvertFromJson |
将JSON转换为MessagePack二进制。 |
MessagePack for C#基本上使用IBufferWriter <byte>进行序列化,并使用ReadOnlySequence <byte> 或Memory <byte>进行反序列化。提供了方便的方法重载来方便地与常见的缓冲区类型和.NET Stream 类配合使用,但这些便捷重载需要复制缓冲区一次,因此有一定的开销。
高级API内部使用内存池来避免不必要的内存分配。如果结果大小小于64K,则只为返回字节分配GC内存。
每个serialize / deserialize方法都可选地接受一个MessagePackSerializerOptions参数,可用于指定要使用的自定义IFormatterResolver或激活LZ4压缩支持。
单个Stream上的多个MessagePack结构
要反序列化包含多个连续MessagePack数据结构的流,您可以使用MessagePackStreamReader类来高效地识别每个数据结构的ReadOnlySequence <byte> 并对其进行反序列化。例如:
static async Task<List<T>> DeserializeListFromStreamAsync<T>(Stream stream, CancellationToken cancellationToken)
{
var dataStructures = new List<T>();
using (var streamReader = new MessagePackStreamReader(stream))
{
while (await streamReader.ReadAsync(cancellationToken) is ReadOnlySequence<byte> msgpack)
{
dataStructures.Add(MessagePackSerializer.Deserialize<T>(msgpack, cancellationToken: cancellationToken));
}
}
return dataStructures;
}
低级API (IMessagePackFormatter<T>)
IMessagePackFormatter<T> 接口负责序列化一个唯一的类型。例如,Int32Formatter : IMessagePackFormatter<Int32>表示Int32 MessagePack 序列化器
public interface IMessagePackFormatter<T>
{
void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options);
T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options);
}
有许多内置格式器位于MessagePack.Formatters下。你的自定义类型通常会自动得到支持,并使用动态代码生成来生成新IMessagePackFormatter<T>类型的内置类型解析器。对于不支持此功能的平台,请参阅我们的AOT代码生成支持。
然而,一些类型(特别是由第三方库或运行时本身提供的类型)无法适当地注解,无合同序列化会产生效率低下甚至错误的结果。为了更好地控制此类自定义类型的序列化,编写您自己的IMessagePackFormatter<T>实现。下面是一个这样的自定义格式器实现的例子。请注意它使用的原始API,该API在下一节中描述。
/// <summary>Serializes a <see cref="FileInfo" /> by its full path as a string.</summary>
public class FileInfoFormatter : IMessagePackFormatter<FileInfo>
{
public void Serialize(
ref MessagePackWriter writer, FileInfo value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}
writer.WriteString(value.FullName);
}
public FileInfo Deserialize(
ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
options.Security.DepthStep(ref reader);
var path = reader.ReadString();
reader.Depth--;
return new FileInfo(path);
}
}
DepthStep 和 Depth--语句提供了一层安全保护,在反序列化不受信任的数据时,可能会导致拒绝服务攻击,发送会导致堆栈溢出异常的消息包数据,并崩溃进程。这两个语句应该包围任何IMessagePackFormatter<T>.Deserialize 方法的主要部分。
注意:一个message pack 格式器必须读写一个数据结构。在上述例子中我们只是读取或写入一个字符串。如果你有多个元素要写入,必须在前面加上map或array头。在反序列化时必须读取整个map/array。例如:
public class MySpecialObjectFormatter : IMessagePackFormatter<MySpecialObject>
{
public void Serialize(
ref MessagePackWriter writer, MySpecialObject value, MessagePackSerializerOptions options)
{
if (value == null)
{
writer.WriteNil();
return;
}
writer.WriteArrayHeader(2);
writer.WriteString(value.FullName);
writer.WriteString(value.Age);
}
public MySpecialObject Deserialize(
ref MessagePackReader reader, MessagePackSerializerOptions options)
{
if (reader.TryReadNil())
{
return null;
}
options.Security.DepthStep(ref reader);
string fullName = null;
int age = 0;
// Loop over *all* array elements independently of how many we expect,
// since if we're serializing an older/newer version of this object it might
// vary in number of elements that were serialized, but the contract of the formatter
// is that exactly one data structure must be read, regardless.
// Alternatively, we could check that the size of the array/map is what we expect
// and throw if it is not.
int count = reader.ReadArrayHeader();
for (int i = 0; i < count; i++)
{
switch (i)
{
case 0:
fullName = reader.ReadString();
break;
case 1:
age = reader.ReadInt32();
break;
default:
reader.Skip();
break;
}
}
reader.Depth--;
return new MySpecialObject(fullName, age);
}
}
unity 支持
Unity最低支持版本为2018.3,API 兼容级别同时支持.NET 4.x 和.NET Standard 2.0。
可以从Release 页面安装unitypackage。如果构建目标是.NET Framework 4.x 并在mono上运行,可以直接使用它。但如果构建目标是IL2CPP,则不能使用Dynamic***Resolver,因此需要使用预代码生成。请参阅预代码生成部分。
MessagePack for C# 包含一些NuGet中原先提供的额外System.*.dll库。它们位于Plugins文件夹下。如果其他包使用这些库(例如使用System.Runtime.CompilerServices.Unsafe.dll的Unity Collections包),为了避免冲突,请删除Plugins下的DLL。
目前CompositeResolver.Create在IL2Cpp上不起作用,因此建议使用StaticCompositeResolver.Instance.Register。
在Unity中,MessagePackSerializer可以使用内置扩展UnityResolver将Vector2、Vector3、Vector4、Quaternion、Color、Bounds、Rect、AnimationCurve、Keyframe、Matrix4x4、Gradient、Color32、RectOffset、LayerMask、Vector2Int、Vector3Int、RangeInt、RectInt、BoundsInt及其可空、数组和列表类型序列化。它默认包含在StandardResolver中。
MessagePack for C# 还有一个额外的不安全扩展。UnsafeBlitResolver 是一个特殊解析器,用于结构数组的极高但不安全的序列化/反序列化