使用protobuf-net进行枚举序列化

本文关键字:枚举 序列化 protobuf-net 使用 | 更新日期: 2023-09-27 18:15:42

我在一个大型项目中将protobuf的旧版本更新为当前版本(使用的版本大约有1-2年的历史)。我不知道牧师)。遗憾的是,新版本抛出了一个异常

CreateWireTypeException在ProtoReader.cs行292

在以下测试用例中:

    enum Test
    {
        test1 = 0,
        test2
    };
    static public void Test1()
    {
        Test original = Test.test2;
        using (MemoryStream ms = new MemoryStream())
        {
            Serializer.SerializeWithLengthPrefix<Test>(ms, original, PrefixStyle.Fixed32, 1);
            ms.Position = 0;
            Test obj;
            obj = Serializer.DeserializeWithLengthPrefix<Test>(ms, PrefixStyle.Fixed32);
        }
    }

我发现枚举不应该在类之外直接序列化,但是我们的系统太大了,不能简单地将所有枚举包装在类中。这个问题还有别的解决办法吗?只有在DeserializeWithLengthPrefix抛出异常时,序列化和反序列化才能正常工作。

测试用例在旧版本中工作良好,例如protobuf-net的r262。

使用protobuf-net进行枚举序列化

很简单,一个bug;这个问题在r640中得到了修复(现在部署到NuGet和google代码中),同时还有一个基于上面代码的额外测试,这样它就不会爬回来了。


Re performance (comments);我要看的第一个提示是:"prefer groups"。基本上,protobuf规范包括两种包含子对象的不同方式——"组"answers"长度-前缀"。Groups是最初的实现,但谷歌现在已经转向"长度前缀",并试图建议人们不要使用"Groups"。然而!由于protobuf-net的工作原理,编写"组"实际上明显更便宜;这是因为不像google的实现, protobuf-net没有提前知道东西的长度。这意味着要编写长度前缀,它需要执行以下操作之一:

  • 根据需要计算长度(几乎与实际序列化数据一样多的工作,但要添加一个完整的代码副本);写入长度,然后实际上序列化数据
  • 序列化到缓冲区,写入长度,写入缓冲区
  • 留下一个占位符,序列化,然后循环并将实际长度写入占位符,如果需要调整填充

我在不同的时间实现了这三种方法,但v2使用了第三种选择。我一直在考虑添加第四个实现:

  • 留下一个占位符,序列化,然后循环并使用超长形式写入实际长度(因此不需要填充调整)

但是…共识似乎是,"过长的形式"有点冒险;尽管如此,protobuf-net和protobuf-net之间还是可以很好地工作的。

但是你可以看到:length-prefix总是有一些开销。现在想象一下嵌套相当深的对象,您可以看到一些光点。群体的工作方式非常不同;组的编码格式为:

  • 写一个开始标记;序列化;写一个结束标记

就是这样;不需要长度;写起来真的非常非常便宜。在网络上,它们之间的主要区别是:

  • 组:写入成本低,但如果遇到意外数据,则不能跳过它们;你必须解析有效负载
  • 的报头
  • length-prefix:写入成本更高,但如果遇到意外数据则可以跳过-只需读取长度并复制/移动那么多字节

但是!太多细节了!

这对你意味着什么?假设你有:

[ProtoContract]
public class SomeWrapper
{
    [ProtoMember(1)]
    public List<Person> People { get { return people; } }
    private readonly List<Person> people = new List<Person>();
}

你可以做超级复杂的改变:

[ProtoContract]
public class SomeWrapper
{
    [ProtoMember(1, DataFormat=DataFormat.Group)]
    public List<Person> People { get { return people; } }
    private readonly List<Person> people = new List<Person>();
}

,它将使用更便宜的编码方案。只要您使用protobuf-net,您现有的所有数据都将很好。