【问题标题】:How to serialize/deserialize large list of items with protobuf-net如何使用 protobuf-net 序列化/反序列化大量项目
【发布时间】:2012-01-06 12:35:42
【问题描述】:

我有一个大约 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 记录,然后其余的都是空记录——我取回了 Price 对象,但所有字段都初始化为默认值。

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

【问题讨论】:

  • 你搞定了吗?您需要更完整的示例吗?

标签: serialization protobuf-net


【解决方案1】:

好消息! protobuf-net API 正是针对这种情况设置的。您应该会看到与IEnumerable&lt;T&gt; 一起使用的一对 SerializeItems 和 DeserializeItems 方法,允许输入和输出流。提供枚举的最简单方法是通过源数据上的“迭代器块”。

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

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

【讨论】:

    【解决方案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)
    

    【讨论】:

      【解决方案3】:

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

      这里有一些最新的信息应该会有所帮助:

      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
      }
      

      【讨论】:

        猜你喜欢
        • 2012-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多