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

时间:2013-05-28 23:36:40

标签: c# protocol-buffers protobuf-net

我在一个巨大的项目中将旧版本的protobuf更新为当前版本(使用的版本大约是1-2岁。我不知道转速)。 可悲的是,新版本引发了异常

  

ProtoReader.cs第292行中的CreateWireTypeException

在以下测试用例中:

    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。

1 个答案:

答案 0 :(得分:3)

简单地说,一个bug;这已在r640中修复(现在部署到NuGet和谷歌代码),以及基于上面代码的额外测试,以便它不会再回来。


表现(评论);我想看的第一个提示是:“偏好群组”。基本上,protobuf规范包括两种不同的方式来包括子对象 - “组”和“长度前缀”。群组是原始实施,但谷歌现在已经转向“长度前缀”,并试图建议人们不要使用“群组”。然而!由于protobuf-net的运作方式,“群体”的写作实际上要便宜得多;这是因为与谷歌实施不同,protobuf-net确实知道事情的长度。这意味着要编写长度前缀,需要执行以下操作之一:

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

我在不同时间实现了所有3种方法,但v2使用了第3种方法。我一直在努力添加第四个实现:

  • 留下占位符,序列化,然后循环返回并使用超长表单写出实际长度(因此不需要填充调整)
但是......一致认为“超长形式”有点危险;不过,它可以很好地用于protobuf-net到protobuf-net。

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

  • 写一个开始标记;连载;写一个结束标记

就是这样;不需要长度;写真的,真的,真的很便宜。在电线上,它们之间的主要区别是:

  • 群组:写作便宜,但如果您将其视为意外数据,则无法跳过它们;你必须解析有效载荷的标题
  • 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,所有现有数据都可以。