使用协议缓冲区和内部数据模型

Cat*_*hwa 15 java datamodel protocol-buffers

我有一个现有的内部数据模型Picture,如下:

package test.model;
public class Picture {

  private int height, width;
  private Format format;

  public enum Format {
    JPEG, BMP, GIF
  }

  // Constructor, getters and setters, hashCode, equals, toString etc.
}
Run Code Online (Sandbox Code Playgroud)

我现在想用协议缓冲区序列化它.我编写了一个Picture.proto文件,它反映了Picture类的字段,并test.model.protobuf使用类名为PictureProtoBuf:在包下编译代码:

package test.model.protobuf;

option java_package = "test.model.protobuf";
option java_outer_classname = "PictureProtoBuf";

message Picture {
  enum Format {
    JPEG = 1;
    BMP = 2;
    GIF = 3;
  }
  required uint32 width = 1;
  required uint32 height = 2;
  required Format format = 3;
}
Run Code Online (Sandbox Code Playgroud)

现在我假设如果我有一个Picture我要序列化并发送到某个地方我必须创建一个PictureProtoBuf对象并映射所有字段,如下所示:

Picture p = new Picture(100, 200, Picture.JPEG);
PictureProtoBuf.Picture.Builder output = PictureProtoBuf.Picture.newBuilder();
output.setHeight(p.getHeight());
output.setWidth(p.getWidth());
Run Code Online (Sandbox Code Playgroud)

当我在我的数据模型中进行枚举时,我才会失败.我现在使用的丑陋方式是:

output.setFormat(PictureProtoBuf.Picture.Format.valueOf(p.getFormat().name());
Run Code Online (Sandbox Code Playgroud)

但是,这很容易破坏并且依赖于我的内部数据模型和协议缓冲区数据模型之间的枚举名称一致(这不是一个很好的假设,因为.proto文件中的枚举名称必须是唯一的).如果.name()来自内部模型的调用与protobuf生成的枚举名称不匹配,我可以看到我必须在枚举上手工制作switch语句.

我想我的问题是我是否以正确的方式解决这个问题?我是否应该废弃我的内部数据模型(test.model.Picture)以支持protobuf生成的one(test.model.protobuf.PictureProtoBuf)?如果是这样,我怎么能实现一些我在我的内部数据模型做了细微之处(如hashCode(),equals(Object),toString(),等)?

Lou*_*man 5

如果您可以控制内部数据模型,则可以进行修改test.model.Picture,以使枚举值知道其对应的protobuf等效项,可能将对应关系传递给枚举构造函数。

例如,使用番石榴 BiMap(具有唯一值的双向地图),我们得到类似

enum ProtoEnum { // we don't control this
  ENUM1, ENUM2, ENUM3;
}

enum MyEnum {
  ONE(ProtoEnum.ENUM1), TWO(ProtoEnum.ENUM2), THREE(ProtoEnum.ENUM3);

  static final ImmutableBiMap<MyEnum, ProtoEnum> CORRESPONDENCE;

  static {
    ImmutableBiMap.Builder<ProtoEnum, MyEnum> builder = ImmutableBiMap.builder();
    for (MyEnum x : MyEnum.values()) {
      builder.put(x.corresponding, x);
    }
    CORRESPONDENCE = builder.build();
  }

  private final ProtoEnum corresponding;

  private MyEnum(ProtoEnum corresponding) {
    this.corresponding = corresponding;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,如果我们要查找MyEnum与a对应的内容ProtoEnum,我们就这样做MyEnum.CORRESPONDENCE.get(protoEnum),而要走另一条路,我们就要做MyEnum.CORRESPONDENCE.inverse().get(myEnum)or myEnum.getCorresponding()

  • 有一个存根给你;很清楚吗? (2认同)

Cat*_*hwa 5

虽然现有的答案都很好,但我还是决定采用Marc Gravell的建议去研究protostuff.

您可以使用protostuff 运行时模块和动态ObjectSchema在运行时为内部数据模型创建模式

我的代码现在缩减为:

// Do this once
private static Schema<Picture> schema = RuntimeSchema.getSchema(Picture.class);
private static final LinkedBuffer buffer = LinkedBuffer.allocate(DEFAULT_BUFFER_SIZE);

// For each Picture you want to serialize...
Picture p = new Picture(100, 200, Picture.JPEG);
byte[] result = ProtobufIOUtil.toByteArray(p, schema, buffer);
buffer.clear();
return result;
Run Code Online (Sandbox Code Playgroud)

当您的内部数据模型中有大量属性时,这是对Google protobuf库(请参阅我的问题)的一个很大改进.我也无法检测速度惩罚(无论如何,我的用例!)