有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我有一个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.
}

现在我想使用protocol buffers对其进行序列化。我写了一幅画。镜像Picture类的字段并在test.model.protobuf包下编译代码(类名为PictureProtoBuf)的proto文件:

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

现在我假设如果我有一个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());

当我的数据模型中有一个枚举时,我就要崩溃了。我现在使用的丑陋方式是:

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

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

我想我的问题是,我的做法是否正确?我是否应该放弃我的内部数据模型(test.model.Picture),转而使用protobuf生成的模型(test.model.protobuf.PictureProtoBuf)?如果是这样,我如何实现我在内部数据模型中所做的一些细节(例如hashCode()equals(Object)toString(),等等)


共 (3) 个答案

  1. # 1 楼答案

    一种方法是只保留生成的枚举:

    package test.model;
    public class Picture {
    
      private int height, width;
      private PictureProtoBuf.Picture.Format format;
    
     // Constructor, getters and setters, hashCode, equals, toString etc.
    }
    

    我已经用过好几次了,这对你来说可能有意义,也可能没有意义。不过,永远不建议在数据模型中使用protobuf生成的类(或扩展它们以添加功能)

  2. # 2 楼答案

    如果可以控制内部数据模型,可以修改test.model.Picture,以便枚举值知道其对应的protobuf等价物,可能会将对应关系传递给枚举构造函数

    例如,使用Guava's^{}(具有唯一值的双向映射),我们得到

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

    然后如果我们想查找对应于a ProtoEnumMyEnum,我们只需要做MyEnum.CORRESPONDENCE.get(protoEnum),反之,我们只需要做MyEnum.CORRESPONDENCE.inverse().get(myEnum)myEnum.getCorresponding()

  3. # 3 楼答案

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

    您可以使用protostuff runtime module和动态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库(参见我的问题)有了很大的改进。我也无法检测到速度损失(无论如何,在我的用例中!)