C#的MessagePack(unity)--02

高级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 是一个特殊解析器,用于结构数组的极高但不安全的序列化/反序列化

相关推荐
玩电脑的辣条哥2 小时前
Python如何播放本地音乐并在web页面播放
开发语言·前端·python
ll7788114 小时前
LeetCode每日精进:20.有效的括号
c语言·开发语言·算法·leetcode·职场和发展
Jackson@ML6 小时前
Python数据可视化简介
开发语言·python·数据可视化
赵琳琅6 小时前
Java语言的云计算
开发语言·后端·golang
lly2024066 小时前
jQuery 杂项方法
开发语言
赵琳琅6 小时前
MDX语言的安全开发
开发语言·后端·golang
开开又心心的学嵌入式7 小时前
C语言——指针进阶应用
c语言·开发语言
开开又心心的学嵌入式7 小时前
C语言——指针基础知识
c语言·开发语言
lonelyhiker7 小时前
javascript的原型链
开发语言·javascript·原型模式
夏梓蕙8 小时前
Elixir语言的软件开发工具
开发语言·后端·golang