如何使用protobuf-net读回附加的对象?

时间:2013-08-24 18:28:38

标签: c# protobuf-net

我正在使用protobuf-net序列化将实时事件附加到文件流中。如何将所有保存的对象重新流式传输以进行分析?我不想使用内存中的集合(因为它会很大)。

private IEnumerable<Activity> Read() {
  using (var iso = new IsolatedStorageFileStream(storageFilename, FileMode.OpenOrCreate, FileAccess.Read, this.storage))
  using (var sr = new StreamReader(iso)) {
    while (!sr.EndOfStream) {
      yield return Serializer.Deserialize<Activity>(iso); // doesn't work
    }
  }
}

public void Append(Activity activity) {
  using (var iso = new IsolatedStorageFileStream(storageFilename, FileMode.Append, FileAccess.Write, this.storage)) {
    Serializer.Serialize(iso, activity);
  }
}

1 个答案:

答案 0 :(得分:3)

首先,我需要讨论protobuf格式(通过Google,而不是protobuf-net)。按设计,它是可附加的,但附加=== merge。对于列表,这意味着“附加为新项目”,但对于单个对象,这意味着“组合成员”。其次,由于上述原因,protobuf中的根对象永远不会终止 - “结束”只是:当您用完传入数据时。第三,并且再次作为直接结果 - 字段不需要以任何特定顺序,并且通常将覆盖。所以:如果您只是多次使用Serialize,然后再读取数据:您将只有一个对象,其中基本上来自流上最后一个对象的值。

但是,您想要做的是一种非常常见的情况。所以protobuf-net通过包含SerializeWithLengthPrefix和DeserializeWithLengthPrefix方法来帮助你。如果使用这些而不是Serialize / Deserialize,则可以正确解析单个对象。基本上,长度前缀限制数据,以便只读取每个对象的确切数量(而不是读取到文件的末尾)。

我强烈建议(作为参数)使用tag === field-number === 1和base-128前缀样式(enum)。除了使数据完全符合protobuf(包括前缀数据)之外,还可以轻松使用额外的辅助方法:DeserializeItems。这通过迭代器块公开每个连续的对象,使得读取大文件非常有效,而不需要同时在内存中存储所有内容。它甚至可以与LINQ一起使用。

还有一种方法可以使用API​​有选择地解析/跳过文件中的不同对象 - 例如,跳过前532条记录而不处理数据。如果你需要一个例子,请告诉我。

如果你已经有很多已经使用Serialize而不是SerializeWithLengthPrefix存储的数据 - 那么可能仍然可以解密数据,通过使用ProtoReader来检测字段数字何时循环回来周围:意思是,给定字段“1,2,4,5,1,3,2,5” - 我们可以得出结论,那里有3个对象并相应地解密。再次,如果您需要一个具体的例子,请告诉我。