Protobuf 流(延迟序列化)API

ash*_*hes 5 xml android json model protocol-buffers

我们有一个 Android 应用程序,它使用协议缓冲区来存储应用程序数据。数据格式(大致)是单个 protobuf(“容器”),其中包含一个 protobuf(“项目”)列表作为重复字段:

message Container {
    repeated Item item = 1;
}
Run Code Online (Sandbox Code Playgroud)

当我们想要保存对项目的更改时,我们必须重新创建 protobuf 容器,将所有项目添加到其中,然后将其序列化并将其写入文件。

这种方法的问题在于它可能会使保存时使用的内存增加三倍,因为数据必须首先从模型类复制到 protobuf 构建器,然后在 protobuf 序列化时复制到字节数组,所有这些都在将其写出到文件流。

我们想要的是一种创建我们的 protobuf 容器并将其延迟序列化到流的方法,然后简单地将每个 protobuf 项(从我们的模型数据创建)添加到序列化并将其写入流的容器中,而不是保留所有项,直到我们在内存中创建了整个容器。

有没有办法构建 protobuf 并将其懒惰地序列化为流?

如果没有办法正式执行此操作,是否有任何图书馆可以提供帮助?有没有人有任何建议或想法如何以其他方式解决这个问题?使这成为可能的替代数据格式或技术(例如包含 protobufs 的 JSON 或 XML)?

Mar*_*ell 5

对于序列化:

protobuf 是一种可追加格式,单个项目被合并,重复项目被追加

因此,要将序列编写为惰性流,您需要做的就是重复编写只有列表中的一项的相同结构:序列化 200 x 的序列“带有 1 项的容器”与序列化 1 x 100% 相同“装有 200 件物品的容器”。

所以:就这样做吧!


对于反序列化:

这在技术上很容易作为流阅读 - 然而,这一切都归结为您使用的库。例如,我在 protobuf-net(.NET/C# 实现)中将Serializer.DeserializeItems<T>其公开为,它读取(完全延迟/流式传输)类型的消息序列T,基于假设它们采用您在问题中描述的形式(Serializer.DeserializeItems<Item>替代的流媒体方式也是如此Serializer.Deserialize<Container>- 最外层的对象在 protobuf 中实际上并不存在)

如果这不可用,但您可以访问原始阅读器 API,则您需要做的是:

  • 为标头读取一个 varint - 这将是值 10 (0x0A),即“(1 << 3) | 2”分别用于字段编号 (1) 和线类型 (2) - 所以这也可能是短语:“从流中读取单个字节,并检查值是否为 10”
  • 为以下项目的长度读取一个 varint
  • 现在:
    • 如果阅读器 API 允许您限制要处理的最大字节数,请使用此长度指定后面的长度
    • 或使用长度限制流包装流 API,限制为该长度
    • 或者只是手动读取那么多字节,然后从有效负载构造一个内存中的流
  • 冲洗,重复


gvd*_*gvd 0

哪有这回事。protobuf 是一个打包结构。为了有效地做到这一点,需要所有数据。您必须自己添加“流协议”。也许每 N 个项目发送一条 protobuf 消息。

  • 实际上,具有重复内部列表的外部对象*是*一个基本序列,并且可以(取决于库)以纯流方式反序列化。甚至有人讨论(尽管从未发生过)将其形式化为允许流式消息处理的 API,旨在通过网络流传递多个异构消息 - 并且其布局与中的布局**相同**问题 (2认同)
  • 当谈到写作时,**每个**图书馆都可以做到这一点。由于 protobuf 是可追加/可合并的,所以不可能不这样做 (2认同)