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

2022-09-03 15:08:36

我有一个现有的内部数据模型,如下所示: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.
}

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

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;
}

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

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

当我的数据模型中有一个枚举时,我就会陷入困境。我现在使用的丑陋方式是:

output.setFormat(PictureProtoBuf.Picture.Format.valueOf(p.getFormat().name());

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

我想我的问题是我是否以正确的方式去做?我是否应该放弃我的内部数据模型()以支持原型buf生成的模型()?如果是这样,我如何实现我在内部数据模型中所做的一些细节(例如,,,等)?test.model.Picturetest.model.protobuf.PictureProtoBufhashCode()equals(Object)toString()


答案 1

虽然现有的答案很好,但我决定进一步研究Marc Gravell的建议,研究原型。

您可以使用 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;

与Google protobuf库相比,这是一个很大的改进(请参阅我的问题),因为您的内部数据模型中有很多很多属性。我也没有可以检测到的速度损失(无论如何,通过我的用例!


答案 2

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

例如,使用番石榴的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;
  }
}

然后如果我们想查找对应的一个,我们就做,而走另一条路,我们就做或。MyEnumProtoEnumMyEnum.CORRESPONDENCE.get(protoEnum)MyEnum.CORRESPONDENCE.inverse().get(myEnum)myEnum.getCorresponding()


推荐