【问题标题】:Protobuf streaming (lazy serialization) APIProtobuf 流式处理(延迟序列化)API
【发布时间】:2012-11-05 23:53:03
【问题描述】:

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

message Container {
    repeated Item item = 1;
}

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

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

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

有没有办法构建一个 protobuf 并将其延迟序列化为流?

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

【问题讨论】:

  • (编辑我的答案以包括序列化和反序列化)

标签: android xml json model protocol-buffers


【解决方案1】:

对于序列化:

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

因此,要将序列写为惰性流,您需要做的就是重复写入相同的结构,列表中只有一个项目:序列化 200 x“包含 1 个项目的容器”的序列与 100% 相同序列化 1 个“包含 200 个项目的容器”。

所以:就这样做吧!


对于反序列化:

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

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

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

【讨论】:

  • 您似乎在谈论反序列化。我有充分的权威表明 OP 正在谈论 序列化
  • 不知道有没有支持这个的Java库?
  • @bacar 抱歉,但我不使用 Java - 我不熟悉暴露的 API
  • 谢谢。我注意到protostuff 声称支持流媒体。虽然还没有玩过它,但我实际上并不完全清楚 protostuff 实际上 ,从项目页面 - Google 的 java protobuf 库的扩展,或替代它。
  • @bacar 据我了解(没有使用过),它允许与 pojo 类型等一起使用。类似于 protobuf-net 在 .net 中所做的
【解决方案2】:

没有这样的事情。 protobuf 是一种压缩结构。为了有效地做到这一点,它需要所有的数据。您必须自己添加“流协议”。也许每 N 个项目发送一个 protobuf msg。

【讨论】:

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

在协议缓冲区的普通 java 版本中,有一个分隔文件,您可以在其中一次写入一个协议缓冲区。我不确定它是否在Android版本中

 aLocation.writeDelimitedTo(out);

正如 Marc 所指出的,它很容易实现;只写一个长度 序列化的字节。在 prortocol-buffers 的普通(非 android)java 版本中,您也可以这样做(您必须序列化为字节数组或类似的东西)

private CodedOutputStream codedStream = null;


public void write(byte[] bytes) throws IOException {
    if (bytes != ConstClass.EMPTY_BYTE_ARRAY) {
        codedStream.writeRawVarint32(bytes.length);
        codedStream.writeRawBytes(bytes);
        codedStream.flush();
    }
}

    private CodedInputStream coded;

public byte[] read() throws IOException {
    if (coded == null) {
        throw new IOException("Reader has not been opened !!!");
    }
    if (coded.isAtEnd()) {
        return null;
    }
    return coded.readBytes().toByteArray();

在其他 Protocol-Buffers 版本中可能会发生一些事情

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    相关资源
    最近更新 更多