Работа с буфером протокола и внутренними моделями данных - PullRequest
14 голосов
/ 14 февраля 2012

У меня есть существующая внутренняя модель данных для 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, который отражает поля класса 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;
}

Теперь я сейчаспри условии, что если у меня есть 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 должны быть уникальными).Я вижу, что мне приходится вручную создавать операторы switch для перечислений, если вызов .name() из внутренней модели не соответствует имени перечисления, сгенерированному с помощью protobuf. * ​​1022 *

Я предполагаю, что мой вопрос:об этом правильный путь?Я должен отказаться от моей внутренней модели данных (test.model.Picture) в пользу сгенерированной протобуфом (test.model.protobuf.PictureProtoBuf)?Если да, как я могу реализовать некоторые тонкости, которые я сделал в моей внутренней модели данных (например, hashCode(), equals(Object), toString() и т. Д.)?

Ответы [ 3 ]

5 голосов
/ 20 февраля 2012

Несмотря на то, что существующие ответы хороши, я решил пойти дальше с предложением Марка Гравелла изучить прототип.

Вы можете использовать модуль времени выполнения 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;

Это большое улучшение по сравнению с библиотекой protobuf Google (см. мой вопрос), когда у вас есть много-много атрибутов в вашей внутренней модели данных.Также не может быть обнаружена потеря скорости (в любом случае, в случае использования!)

4 голосов
/ 14 февраля 2012

Если у вас есть контроль над внутренней моделью данных, вы можете изменить 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;
  }
}

, а затем, если мы хотим посмотреть MyEnum, соответствующийна ProtoEnum мы просто делаем MyEnum.CORRESPONDENCE.get(protoEnum), а чтобы пойти другим путем, мы просто делаем MyEnum.CORRESPONDENCE.inverse().get(myEnum) или myEnum.getCorresponding().

1 голос
/ 15 февраля 2012

Один из способов - сохранить только сгенерированное перечисление:

package test.model;
public class Picture {

  private int height, width;
  private PictureProtoBuf.Picture.Format format;

 // Constructor, getters and setters, hashCode, equals, toString etc.
}

Я использовал это несколько раз, это может иметь или не иметь смысла в вашем случае. Однако никогда не рекомендуется использовать сгенерированные protobuf классы в качестве модели данных (или расширить их для добавления функциональности).

...