如何高效地在ProtoBuf中建模HashMap/字典。

21
我有一个由.NET代码序列化的protobuf文件,我想将其用Java消费。在.NET代码中,有Dictionary数据类型,proto模式看起来像这样:
message Pair {
   optional string key = 1;
   optional string value = 2;
}

message Dictionary {
   repeated Pair pairs = 1;
}

正如在stackoverflow帖子Protocol Buffers中的字典中所描述的那样。
我可以使用protoc将proto文件编译成Java类。我可以成功地将protobuf文件反序列化为Java对象。唯一的问题是,它在Java中被转换为Pair对象的List而不是HashMap。当然,我仍然拥有所有数据,但我无法像我希望的那样高效地访问数据。如果我有键的值,我必须遍历整个列表才能获得其相应的值。这似乎不是最优的方法。
我想知道是否有更好的方法来建模protobuf中的Dictionary/Map数据类型。
谢谢 更新: 我尝试了Jon Skeet的建议,在addressbook示例中添加了map类型字段,但仍然遇到了问题。
message Person {
  required string name = 1;
  required int32 id = 2;        // Unique ID number for this person.
  optional string email = 3;
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }
  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }
  repeated PhoneNumber phone = 4;
  map<string, string> mapdata = 5;
}

编译时,协议出现错误。

addressbook.proto:25:3: Expected "required", "optional", or "repeated".
addressbook.proto:25:6: Expected field name.

根据Google protobuf文档,proto 2确实支持map类型https://developers.google.com/protocol-buffers/docs/proto#maps。正如我引用的那样,

Maps不能是可重复的、可选的或必需的。

所以我真的不知道为什么protoc无法编译它。这里还有另一个讨论have to create java pojo for the existing proto includes Map。答案表明map只是proto 3的一个特性。这与谷歌的文档相矛盾。

如果您正在使用proto3,您可以使用Map。https://developers.google.com/protocol-buffers/docs/proto3?hl=en#maps - Andy Turner
@AndyTurner:在proto2中也支持地图。它们与proto3同时引入,但您不必使用proto3语法文件才能使用它们。 - Jon Skeet
1个回答

42

好的,自3.0版本以来,“protobuf proper”已经支持了映射。例如,您的proto实际上是:


message Dictionary {
    map<string, string> pairs = 1;
}

好消息是,通过你定义的键和值字段,这与你现有的数据完全向后兼容 :)

坏消息是,我不知道protobuf-net是否支持它。如果你在.NET方面实际上没有使用.proto文件,并且在声明方式下完成所有操作,你可能只需要修改.proto文件,重新生成Java代码,然后就可以继续了...

剩下的坏消息是,映射是在v3.0中引入的,而此时仍处于alpha/beta阶段。现在,取决于你需要何时发货,你可能会决定押注v3.0在你需要它的时候发布 - 在我看来,拥有漂亮的映射语法的好处非常显着。目前正在进行的大多数更改都是关于新的proto3特性 - 而映射也允许在proto2语法文件中使用... 只是你需要v3.0编译器和运行时才能使用它们。


谢谢,Jon。是的,我认为protobuf-net不支持它。如果有字典类型,它会生成重复的键值对类型,而不是映射。正如你猜测的那样,protobuf在.NET端被定义为注释。我们必须使用protobuf-net从代码生成.proto文件,并手动进行修改(很多)。我将验证向后兼容性部分并回报。 - Lan
我尝试了您的建议,在通讯录示例中添加了一个地图字段。但是protoc会抛出错误addressbook.proto:31:5:期望“required”、“optional”或“repeated”。addressbook.proto:31:8:期望字段名称。我的protoc版本是2.6.1。请参阅http://stackoverflow.com/questions/29407123/have-to-create-java-pojo-for-the-existing-proto-includes-map,并且它报告了相同的问题。 - Lan
@Lan:你需要更近期版本的protoc。我会进行编辑以澄清这一点。 - Jon Skeet
在邮件列表上阅读了一些帖子后,我得出了相同的结论。https://groups.google.com/forum/#!topic/protobuf/p4WxcplrlA4。下载了protobuf 3 alpha 3之后,我可以让映射正常工作。而且它似乎是向后兼容的。感谢您的帮助。 - Lan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接