如何使用protobuf-net序列化/反序列化大型项目列表

时间:2011-11-25 17:18:35

标签: serialization protobuf-net

我有大约5亿件物品的清单。如果我序列化单个项目而不是列表,我可以将它序列化为带有protobuf-net文件的文件 - 我无法将项目收集到价格列表中,然后序列化,因为我的内存不足。所以,我必须一次序列化一条记录:

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对象,但所有字段都被初始化为默认值。

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

3 个答案:

答案 0 :(得分:4)

好消息! protobuf-net API就是针对这种情况而设置的。您应该看到一个与IEnumerable<T>一起使用的SerializeItems和DeserializeItems方法,允许流入和流出。最简单的方法是通过源数据的“迭代器块”来为它提供枚举。

如果出于某种原因,这是不方便的,那就是在每个项目的基础上使用SerializeWithLengthPrefix和DeserializeWithLengthPrefix 100%相同,指定(作为参数)字段:1和prefix-style:base-128。您甚至可以使用SerializeWithLengthPrefix进行写入,并使用DeserializeItems进行读取(只要使用字段1和base-128)。

重新举例 - id必须在完全可重现的场景中看到评论;实际上,我会期望,只有一个对象退出,包含每个对象的组合值 - 因为没有长度前缀,protobuf规范假定你只是连接值到单个对象。上面提到的两种方法避免了这个问题。

答案 1 :(得分:2)

可能是我为时已晚......但只是为了补充Marc已经说过的话。

当您使用Serializer.Serialize(output, price); protobuf将连续消息视为(相同)单个对象的一部分时。因此,当您使用

进行反序列化时
while ((price = Serializer.Deserialize<Price>(input)) != null)

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

要执行您要执行的操作,请将序列化代码更改为:

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

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

答案 2 :(得分:0)

自Marc回答以来,API显然发生了变化 似乎没有Seri​​alizeItems方法了。

以下是一些应该有帮助的最新信息:

ProtoBuf.Serializer.Serialize(stream, items);
如上所示,

可以采用IEnumerable,它在序列化时可以完成工作 然而,有一个DeserializeItems(...)方法和魔鬼在细节:) 如果像上面那样序列化IEnumerable,那么你需要调用DeserializeItems传递PrefixStyle.Base128和1作为fieldNumber,因为这些是默认值。
这是一个例子:

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
}