Я переношу Java-приложение из буферов протокола 2 в буфер протокола 3.
В proto 2, чтобы проверить, установлено ли поле, у вас есть метод hasfield()
для которого сгенерирован пример кода Java:
public boolean hasText() {
return ((bitField0_ & 0x00000004) == 0x00000004);
}
Однако в proto 3 он был удален. Как вы проверяете, установлено ли поле в proto 3?
Да, proto3 облажался со своей оптимизацией, так как скаляры со значением по умолчанию не сериализуются. Теперь читатель не может различить «поле не указано» и «поле указано, но со значением по умолчанию». Вы можете вернуть эту функциональность, заключив свой скаляр в oneof или внутри подсообщения с сопутствующей грубостью, присущей вашему предпочтительному языку.




Один из предлагаемых подходов - здесь:
# NOTE: As of proto3, HasField() only works for message fields, not for
# singular (non-message) fields. First try to use HasField and
# if it fails (with a ValueError) we manually consult the fields.
try:
return message_pb.HasField(property_name)
except ValueError:
all_fields = set([field.name for field in message_pb._fields])
return property_name in all_fields
Также с той же страницы:
In proto3, field presence for scalar fields simply doesn't exist. Your mental model for proto3 should be that it's a C++ or Go struct. For integers and strings, there is no such thing as being set or not, it always has a value. For submessages, it's a pointer to the submessage instance which can be NULL, that's why you can test presence for it.
Да, если вы хотите наличие / отсутствие для скаляров, вам нужно либо заключить их в oneof, либо внутри сообщения (например, - google.protobuf.wrappers.proto).
Я думаю, что рекомендуемый подход, который не идеален из-за проектных решений, принятых в proto3, - это проверка стандартных значений. Вы не можете явно проверить, установлено ли поле или нет. Поскольку доступ к msg._fields не рекомендуется, как описано здесь, остается только проверить, установлено ли в поле его стандартное значение:
if msg.textfield.isEmpty() {
//assume textfield is not set
}
Это может сработать. Вы также можете обернуть его в oneof. oneof test_string { string name = 1; }. Здесь тоже есть свои подводные камни.
Лучшее предложение, которое я видел в proto3, - обернуть ваше поле в singleton oneof. Это позволит вам снова проверить наличие / отсутствие, аналогично proto2.
message blah
{
oneof foo_ { sint32 foo = 1; }
}
В коде, сгенерированном Python, это очень гладко, поскольку с foo можно напрямую работать как с скаляром, как если бы он не находился внутри oneof.
К сожалению, для Java я думаю, что поддержка oneof гораздо хуже. Google также намеренно удалил функцию класса, сгенерированную hasFoo (), в proto3. Таким образом, вам нужно будет проконсультироваться с getFooCase () oneof вместо того, чтобы проверить наличие или отсутствие.
https://developers.google.com/protocol-buffers/docs/reference/java-generated#oneof-fields
Да, я понимаю, что это означает массу неприятностей и сопутствующих хлопот, которые они приносят. С другой стороны, на проводе нет накладных расходов.
Второе лучшее предложение, которое я видел, - использовать вложенное сообщение для обертывания скаляра, потому что наличие / отсутствие вложенных сообщений все еще поддерживается. В google.protobuf.wrappers.proto есть хорошо известные типы (WKT). Если вы их используете, вы можете даже получить дополнительную специальную обработку на предпочитаемом вами языке, где обернутый скаляр может быть легко обработан, как если бы содержащееся подсообщение не существовало (или, как я читал, сам не совсем уверен в этом вопросе ).
hasField()Я вижу, что некоторые люди предлагают использовать oneof, чтобы обернуть в него ваше поле, но proto3 уже предоставляет встроенный wrappers с методами типа hasField для примитивных типов данных, которые позволит вам проверить, установлено ли поле или нет.
Вот как это можно объявить (см. Подробнее Типы значений здесь):
syntax = "proto3";
import "google/protobuf/wrappers.proto";
message MyProtoMessage {
google.protobuf.BoolValue enabled = 1;
google.protobuf.StringValue name = 2;
google.protobuf.Int32Value age = 3;
}
и это в коде Java, чтобы проверить это:
MyProtoMessage myProtoMessage = getItFromSomewhere();
if (myProtoMessage.hasName()) {
// field is set by client
myProtoMessage.getName();
} else {
// field is not set by the client
// but you can still call `myProtoMessage.getName()` which will `return` default value ""
}
Protobuf 3.15.0 теперь поддерживает необязательные поля. Вы можете снова использовать методы hasField.
AFAIK, это просто не отслеживается в реализациях proto3 по умолчанию. Поля либо нулевые (и не отправляются), либо ненулевые (и отправляются), и Это оно.