如何使用protobuf-net对大量项目进行序列化/反序列化

12

我有一个大约500百万项的列表。如果我序列化单个项而不是列表,我可以使用protobuf-net文件将其序列化到文件中--我无法将项目收集到Price列表中然后进行序列化,因为我会耗尽内存。因此,我必须一次序列化一条记录:

using (var input = File.OpenText("..."))
using (var output = new FileStream("...", FileMode.Create, FileAccess.Write))
{
    string line = "";
    while ((line = input.ReadLine()) != null)
    {
        Price price = new Price();
        (code that parses input into a Price record)

        Serializer.Serialize(output, price);
    }
}

我的问题是关于反序列化部分的。看起来Deserialize方法不会把流的位置移动到下一条记录。我尝试了:

using (var input = new FileStream("...", FileMode.Open, FileAccess.Read))
{
    Price price = null;
    while ((price = Serializer.Deserialize<Price>(input)) != null)
    {
    }
}

我看到一个真实的价格记录,而其余记录都是空记录 -- 我收到了Price对象,但所有字段都被初始化为默认值。

如何正确反序列化包含未作为列表序列化的对象列表的流?


你解决了这个问题吗?需要一个更完整的例子吗? - Marc Gravell
3个回答

6

好消息!protobuf-net API已经为这种情况设置好了。您应该看到一对可以处理 IEnumerable<T> 的SerializeItems和DeserializeItems方法,允许流式输入和输出。最简单的方法是通过源数据上的“迭代器块”来枚举它。

如果由于某种原因不方便使用上述方法,那么完全可以使用SerializeWithLengthPrefix和DeserializeWithLengthPrefix来逐个项指定(作为参数)字段:1和前缀样式:base-128。您甚至可以在写入时使用SerializeWithLengthPrefix,在读取时使用DeserializeItems(只要使用字段1和base-128即可)。

关于示例 - 我必须在一个完全可重现的场景中查看它才能发表评论;实际上,我期望您只会得到一个对象,其中包含每个对象的组合值 - 因为没有长度前缀,protobuf规范假设您只是将值连接到单个对象上。以上提到的两种方法避免了这个问题。


4

也许我来晚了,但是想补充一下Marc已经说过的。

当你使用Serializer.Serialize(output, price);时,protobuf会将连续的信息视为同一个对象的一部分。因此,当你使用Deserialize时

while ((price = Serializer.Deserialize<Price>(input)) != null)

您将得到所有的记录。因此,您将只看到最后一个价格记录。

要实现您想要的功能,请将序列化代码更改为:

Serializer.SerializeWithLengthPrefix(output, price, PrefixStyle.Base128, 1);

并且

while ((price = Serializer.DeserializeWithLengthPrefix<Price>(input, PrefixStyle.Base128, 1)) != null)

4

自从Marc的回答以来,API显然已经发生了变化。
似乎不再有SerializeItems方法。

以下是一些更为更新的信息,应该会有所帮助:

ProtoBuf.Serializer.Serialize(stream, items);

上面的代码可以使用IEnumerable并完成序列化工作。
然而,如果你像上面那样序列化IEnumerable,则需要调用DeserializeItems方法,传递PrefixStyle.Base128和1作为字段编号,因为这些是默认值。
以下是示例:

ProtoBuf.Serializer.DeserializeItems<T>(stream, ProtoBuf.PrefixStyle.Base128, 1));

同样,正如Marc和Vic所指出的那样,您可以按照每个项目的方式进行序列化/反序列化,就像这样(使用PrefixStyle和fieldNumber的自定义值):
ProtoBuf.Serializer.SerializeWithLengthPrefix(stream, item, ProtoBuf.PrefixStyle.Base128, fieldNumber: 1);

并且。
T item;
while ((item = ProtoBuf.Serializer.DeserializeWithLengthPrefix<T>(stream, ProtoBuf.PrefixStyle.Base128, fieldNumber: 1)) != null)
{
    // do stuff here
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接