存储一组序列化的protobuf对象

时间:2016-07-12 05:57:03

标签: c++ serialization protocol-buffers separator

我在我的Protobuf对象上使用SerializeToString,然后将该字符串存储在数据库中。

但是,有时我会有一系列这样的对象。我想存储整个序列化数组,为此我需要在序列化字符串之间使用一些分隔符字符串。

根据我所看到的documentation,字符串只是一个字节数组,所以我对它的内容没有任何承诺。

这里最好的方法是什么?

我不知道数组的长度,因为我们可以将对象附加到它上面,并且我希望它在整个过程中都存储在数据库中。

2 个答案:

答案 0 :(得分:3)

假设您的protobuf message看起来像这样:

message Object
{
  ... = 1;
  ... = 2;
  ... = 3;
}

然后在同一个文件中再引入1个message,这是Object的集合。

message Objects
{
  repeated Object array = 1;
}

因此,当您有许多元素时,您可以只使用Objects并在SerializeAsString()上使用Objects。这将节省您单独序列化单个Object并放置您自己的手工分隔符的工作量。您可以使用Object的单个实例序列化所有Objects 使用这种方法,您委派所有解析和&序列化工作也适用于Protobuf库。我在我的项目中使用它,它就像一个魅力。

此外,明智地使用Objects也可以避免制作Object的额外副本。您可以向其添加项目并使用索引进行访问。 protobufs的repeated字段符合C ++ 11,因此您也可以将它与迭代器或增强的for循环一起使用。

需要注意的是,当您将Objects::SerializeAsString()的输出存储到文件中时,应首先输入string的长度,然后输入实际的序列化字符串。在读取时,您可以先读取长度,然后再读取总字节数。为了便于使用,我扩展了std::fstream并重载了以下方法:

struct fstreamEncoded : std::fstream
{
    // other methods
    void  // returns `void` to avoid multiple `<<` in a single line
    operator<< (const string& content)
    {  // below casting would avoid recursive calling of this method
       // adding `length() + 1` to include the protobuf's last character as well
      static_cast<std::fstream&>(*this) << (content.length() + 1) << "\n" << content << std::endl;
    }

    string
    getline ()
    {
      char length_[20] = {};
      std::istream::getline(length_, sizeof(length_) - 1);
      if(*length_ == 0)
        return "";

      const size_t length = std::atol(length_);  // length of encoded input
      string content(length, 0);  // resize the `string`
      read(&content[0], length);  // member of `class istream`
      return content;
    }
}

以上只是一个例子。您可以根据您的项目需求进行跟踪。

答案 1 :(得分:1)

如果您不能保证您的分隔符对序列化数据的内容是唯一的,那么我会在每个对象的开头添加一个大小项,指示后面的字符串有多长。