Мне нужно указать сообщение с необязательным полем в protobuf (синтаксис proto3). С точки зрения синтаксиса proto 2, сообщение, которое я хочу выразить, выглядит примерно так:

message Foo {
    required int32 bar = 1;
    optional int32 baz = 2;
}

Насколько я понимаю, "необязательная" концепция была удалена из синтаксиса proto 3 (вместе с требуемой концепцией). Хотя неясна альтернатива - использование значения по умолчанию, чтобы указать, что поле не было указано отправителем, оставляет неоднозначность, если значение по умолчанию принадлежит домену допустимых значений (рассмотрим, например, логический тип).

Итак, как мне закодировать сообщение выше? Спасибо.

Ответы (8)

Начиная с protobuf выпуска 3.15, proto3 поддерживает использование ключевого слова optional (как и в proto2) для предоставления информации о наличии скалярного поля.

syntax = "proto3";

message Foo {
    int32 bar = 1;
    optional int32 baz = 2;
}

A has_baz ()/hasBaz () метод создается для необязательного поля выше, как это было в proto2.

Под капотом protoc эффективно обрабатывает необязательное поле, как если бы оно было объявлено с использованием оболочки oneof, как Ответ CyberSnoopy предлагается:

message Foo {
    int32 bar = 1;
    oneof optional_baz {
        int32 baz = 2;
    }
}

Если вы уже использовали этот подход, теперь вы можете упростить объявления сообщений (переключитесь с oneof на необязательно) и код, поскольку формат проводов тот же.

Подробные сведения о присутствии поля и необязательном в proto3 можно найти в Примечании к применению: Наличие поля doc.

Историческое примечание: экспериментальная поддержка optional в proto3 впервые была объявлена ​​23 апреля 2020 г. в в этом комментарии. Для его использования требовалось передать протокол - экспериментальный_allow_proto3_optional в выпусках 3.12-3.14.

Один из способов - необязательно, как описано в принятом ответе: https://stackoverflow.com/a/62566052/1803821

Другой - использовать объекты-оболочки. Вам не нужно писать их самостоятельно, так как Google уже предоставляет их:

Вверху вашего .proto файла добавьте этот импорт:

импорт "google / protobuf / wrappers.proto";

Теперь для каждого простого типа можно использовать специальные обертки:

DoubleValue
FloatValue
Int64Value
UInt64Value
Int32Value
UInt32Value
BoolValue
StringValue
BytesValue

Итак, чтобы ответить на исходный вопрос, можно использовать такую ​​оболочку так:

message Foo {
    int32 bar = 1;
    google.protobuf.Int32Value baz = 2;
}

Теперь, например, на Java я могу делать такие вещи, как:

если (foo.hasBaz ()) {...}

Another way to encode the message you intend is to add another field to track "set" fields:

синтаксис = "proto3";

пакет qtprotobuf.examples;

message SparseMessage {
    повторяющиеся поля uint32Used = 1;
    boolhibitedParty = 2;
    uint32 numberOfKids = 3;
    строка nickName = 4;
}

message ExplicitMessage {
    перечисление PARTY_STATUS {ATTENDED = 0; DIDNT_ATTEND = 1; DIDNT_ASK = 2;};
    PARTY_STATUShibitedParty = 1;
    bool указаноKids = 2;
    uint32 numberOfKids = 3;
    перечисление NO_NICK_STATUS {HAS_NO_NICKNAME = 0; WOULD_NOT_ADMIT_TO_HAVING_HAD_NICKNAME = 1;};
    NO_NICK_STATUS noNickStatus = 4;
    строка nickName = 5;
}

Это особенно уместно, если имеется большое количество полей и только небольшое их количество назначено.

В Python использование будет выглядеть так:

импорт field_enum_example_pb2
m = поле_enum_example_pb2.SparseMessage ()
m.attendedParty = True
m.fieldsUsed.append (field_enum_example_pb2.SparseMessages.ATTENDEDPARTY_FIELD_NUMBER)

Основываясь на ответе Кентона, более простое, но рабочее решение выглядит так:

message Foo {
    oneof optional_baz { // "optional_" prefix here just serves as an indicator, not keyword in proto2
        int32 baz = 1;
    }
}

Другой способ - вы можете использовать битовую маску для каждого необязательного поля. и установить эти биты, если значения установлены, и сбросить те биты, значения которых не установлены

enum bitsV {
    baz_present = 1; // 0x01
    baz1_present = 2; // 0x02

}
message Foo {
    uint32 bitMask;
    required int32 bar = 1;
    optional int32 baz = 2;
    optional int32 baz1 = 3;
}

При разборе проверяется значение bitMask.

if (bitMask & baz_present)
    baz is present

if (bitMask & baz1_present)
    baz1 is present

Чтобы расширить предложение @cybersnoopy здесь

, если у вас есть файл .proto с таким сообщением:

message Request {
    oneof option {
        int64 option_value = 1;
    }
}

Вы можете использовать опции case при условии (код, сгенерированный java):

Итак, теперь мы можем написать следующий код:

Request.OptionCase optionCase = request.getOptionCase();
OptionCase optionNotSet = OPTION_NOT_SET;

if (optionNotSet.equals(optionCase)){
    // value not set
} else {
    // value set
}

вы можете узнать, была ли инициализирована одна из них, сравнив ссылки с экземпляром по умолчанию:

GRPCContainer container = myGrpcResponseBean.getContainer();
if (container.getDefaultInstanceForType() != container) {
...
}

В proto3 все поля являются «необязательными» (в том смысле, что это не является ошибкой, если отправитель не может их установить). Но поля больше не являются "допускающими значение NULL", поскольку нет способа отличить поле, явно установленное на значение по умолчанию, и то, что оно не было установлено вообще.

Если вам нужно «нулевое» состояние (и нет значения вне диапазона, которое вы можете использовать для этого), вам вместо этого нужно будет закодировать это как отдельное поле. Например, вы можете сделать:

message Foo {
  bool has_baz = 1;  // always set this to "true" when using baz
  int32 baz = 2;
}

В качестве альтернативы вы можете использовать oneof:

message Foo {
  oneof baz {
    bool baz_null = 1;  // always set this to "true" when null
    int32 baz_value = 2;
  }
}

Версия oneof является более явной и более эффективной в сети, но требует понимания того, как работают значения oneof.

Наконец, еще один вполне разумный вариант - придерживаться proto2. Proto2 не является устаревшим, и на самом деле многие проекты (в том числе внутри Google) очень сильно зависят от функций proto2, которые удалены в proto3, поэтому они, вероятно, никогда не переключатся. Так что в обозримом будущем его можно безопасно использовать.

2022 WebDevInsider