Проверьте, установлено ли поле в буфере протокола 3

Я переношу Java-приложение из буферов протокола 2 в буфер протокола 3.

В proto 2, чтобы проверить, установлено ли поле, у вас есть метод hasfield() для которого сгенерирован пример кода Java:

public boolean hasText() {
  return ((bitField0_ & 0x00000004) == 0x00000004);
}

Однако в proto 3 он был удален. Как вы проверяете, установлено ли поле в proto 3?

AFAIK, это просто не отслеживается в реализациях proto3 по умолчанию. Поля либо нулевые (и не отправляются), либо ненулевые (и отправляются), и Это оно.

Marc Gravell 19.08.2018 17:21

Да, proto3 облажался со своей оптимизацией, так как скаляры со значением по умолчанию не сериализуются. Теперь читатель не может различить «поле не указано» и «поле указано, но со значением по умолчанию». Вы можете вернуть эту функциональность, заключив свой скаляр в oneof или внутри подсообщения с сопутствующей грубостью, присущей вашему предпочтительному языку.

jschultz410 21.02.2020 21:09
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
15
2
23 018
5

Ответы 5

Один из предлагаемых подходов - здесь:

# 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).

jschultz410 21.02.2020 21:05

Я думаю, что рекомендуемый подход, который не идеален из-за проектных решений, принятых в proto3, - это проверка стандартных значений. Вы не можете явно проверить, установлено ли поле или нет. Поскольку доступ к msg._fields не рекомендуется, как описано здесь, остается только проверить, установлено ли в поле его стандартное значение:

if msg.textfield.isEmpty() {
    //assume textfield is not set
}

Это может сработать. Вы также можете обернуть его в oneof. oneof test_string { string name = 1; }. Здесь тоже есть свои подводные камни.

artless noise 07.10.2019 00:54

Лучшее предложение, которое я видел в 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). Если вы их используете, вы можете даже получить дополнительную специальную обработку на предпочитаемом вами языке, где обернутый скаляр может быть легко обработан, как если бы содержащееся подсообщение не существовало (или, как я читал, сам не совсем уверен в этом вопросе ).

Используйте Proto Wrappers 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.

Другие вопросы по теме