Я пытаюсь долго сериализовать кучу объектов, связанных сильной иерархией классов в java, и я хотел бы использовать буферы протокола для этого из-за их простоты, производительности и простоты обновления. Однако они не обеспечивают много поддержки полиморфизма. Прямо сейчас, как я его обрабатываю, это иметь решение "одно сообщение для всех", у которого есть обязательное строковое поле uri, которое позволяет мне создать экземпляр правильного типа с помощью отражения, затем кучу необязательных полей для всех другие возможные классы, которые я мог бы сериализовать, только один из них будет использоваться (в зависимости от значения поля uri). Есть ли лучший способ справиться с полиморфизмом, или это так хорошо, как я собираюсь получить?
Какой правильный способ сделать полиморфизм с буферами протоколов?
Ответ 1
Существует несколько методов для реализации полиморфизма. Я стараюсь их охватить здесь: Полиморфизм буфера протоколов
В моем предпочтительном подходе используется вложенный extensions:
message Animal
{
extensions 100 to max;
enum Type
{
Cat = 1;
Dog = 2;
}
required Type type = 1;
}
message Cat
{
extend Animal
{
required Cat animal = 100; // Unique Animal extension number
}
// These fields can use the full number range.
optional bool declawed = 1;
}
message Dog
{
extend Animal
{
required Dog animal = 101; // Unique Animal extension number
}
// These fields can use the full number range.
optional uint32 bones_buried = 1;
}
Ответ 2
В proto3 заменено ключевое слово extend
.
Из docs: If you are already familiar with proto2 syntax, the Any type replaces extensions.
syntax = "proto3";
import "google/protobuf/any.proto";
message Foo {
google.protobuf.Any bar = 1;
}
Но будьте осторожны: Any
по существу является байтом байта. В большинстве случаев лучше использовать Oneof
:
syntax = "proto3";
message A {
string a = 1;
}
message B {
string b = 1;
}
message Foo {
oneof bar {
A a = 1;
B b = 2;
}
}
Ответ 3
Это не является ответом на исходный вопрос, но может быть полезным для других, использующих v3 Protocol Buffers. Версия 3 запрещает ключевое слово extensions
. Запуск protoc
в следующем файле генерирует ошибку с сообщением Extension ranges are not allowed in proto3
.
syntax = "proto3";
message BaseMessage {
extensions 100 to max;
}
Ответ 4
Решение Jon правильное и работает, но довольно странно (для меня). Но протокольные буферы довольно просты, поэтому вы можете сделать что-то вроде этого:
enum Type {
FOO = 0;
BAR = 1;
}
message Foo {
required Type type = 1;
}
message Bar {
required Type type = 1;
required string text = 2;
}
В основном панель сообщений расширяет сообщение Foo (с практической стороны, конечно). Реализация на Java тоже проста:
Bar bar = Bar.newBuilder().setType(Type.BAR).setText("example").build();
byte[] data = bar.toByteArray();
----
Foo foo = Foo.parseFrom(data);
if(foo.getType() == Type.BAR){
Bar bar = Bar.parseFrom(data);
System.out.println(bar.getText());
}
Я знал, что это не изящное решение, но оно простое и логичное.
Ответ 5
Откроем Расширения и вложенные расширения для немного более чистого способа сделать это.
Ответ 6
Считаете ли вы использование extensions? Вы можете указать поле uri для используемого типа, а затем просто загрузить соответствующие расширения. Если вы знаете, что ваши поля являются взаимоисключающими, вы можете повторно использовать идентификатор поля между отдельными расширениями.
Вы должны справиться с этим сами, потому что буферы протокола не предназначены для самостоятельного описания за пределами простого списка значений. Это затронуто на странице методов Google.
Ответ 7
Решение для меня немного лучше, чем ответить @Łukasz Marciniak.
Если Bar расширяет Foo, просто напишите:
message Bar {
optional Foo foo = 1;
optional double aDouble = 2;
}
message Foo {
optional string aString = 1;
}
Итак, если Foo эволюционирует, изменяется только сообщение Foo.