Обработка нулевых значений в протобуферах

Я работаю над тем, что извлекает данные из базы данных и конструирует сообщение protobuff. Учитывая, что нулевые значения могут быть получены из базы данных для определенных полей, я получу исключение Null-указателя при попытке построить сообщение protobuff. Знакомство с тем, что null не поддерживается в protobuffs из потока http://code.google.com/p/protobuf/issues/detail?id=57, мне интересно, возможен ли только другой способ обработки NPE для вставки ручных проверок в файл java, соответствующий прото, как показано ниже!

message ProtoPerson{
    optional string firstName = 1;
    optional string lastName = 2;
    optional string address1 = 3;
}

ProtoPerson.Builder builder = ProtoPerson.Builder.newBuilder();
if (p.getFirstName() != null) builder.setFirstName(p.getFirstName());
if (p.getLastName() != null) builder.setLastName(p.getLastName());
if (p.getAddress1() != null) builder.setAddress1(p.getAddress1());
...

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

Ответы

Ответ 1

Там нет простого решения. Я бы рекомендовал просто иметь дело с нулевыми проверками. Но если вы действительно хотите избавиться от них, вот несколько идей:

  • Вы можете написать плагин code generator, который добавляет методы setOrClearFoo() к каждому классу Java. Генератор кода Java предоставляет точки вставки для этого (см. Конец этой страницы).
  • Вы можете использовать отражение Java для итерации по методам get*() p, вызвать каждый из них, проверить на null, а затем вызвать метод set*() builder, если не null. Это будет иметь дополнительное преимущество, что вам не придется обновлять свой код копирования каждый раз, когда вы добавляете новое поле, но оно будет намного медленнее, чем писать код, который явно копирует каждое поле.

Ответ 2

Отказ от ответственности: ответ от Googler с использованием protobufs на ежедневной основе. Я никоим образом не представляю Google.

  • Назовите ваш proto Person вместо PersonProto или ProtoPerson. Скомпилированные protobufs - это просто определения классов, определенные языком, который вы используете, с некоторыми улучшениями. Добавление "Прото" - дополнительная многословие.
  • Используйте YourMessage.hasYourField() вместо YourMessage.getYourField() != null. Значение по умолчанию для строки protobuf - это пустая строка, которая делает NOT равной нулю. Принимая во внимание, что независимо от того, будет ли ваше поле отменено или очищено или пустая строка, .hasYourField() всегда возвращает значение false. См. значения по умолчанию для общих типов полей protobuf.
  • Вы, наверное, знаете, но я хочу сказать явно: Не программировать поле protobuf на null. Даже для стороннего protobuf, null вызывает всевозможные проблемы. Вместо этого используйте .clearYourField().
  • Person.Builder класс НЕ имеет метод .newBuilder(). Person класс делает. Поймите шаблон Builder следующим образом: вы создаете новый строитель, только если его еще нет.

Перепишите ваш protobuf:

message Person {
  optional firstName = 1;
  optional lastName = 2;
  optional address1 = 3;
}

Перепишите свою логику:

Person thatPerson = Person.newBuilder()
    .setFirstName("Aaa")
    .setLastName("Bbb")
    .setAddress1("Ccc")
    .build();

Person.Builder thisPersonBuilder = Person.newBuilder()

if (thatPerson.hasFirstName()) {
  thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}

if (thatPerson.hasLastName()) {
  thisPersonBuilder.setLastName(thatPerson.getLastName());
}

if (thatPerson.hasAddress1()) {
  thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}

Person thisPerson = thisPersonBuilder.build();

И если thatPerson - это созданный вами объект person, который имеет значения атрибутов, которые могут быть пустой строкой, пустым пространством или нулем, тогда я бы рекомендовал использовать Guava Strings library:

import static com.google.common.base.Strings.nullToEmpty;

Person.Builder thisPersonBuilder = Person.newBuilder()

if (!nullToEmpty(thatPerson.getFirstName()).trim().isEmpty()) {
  thisPersonBuilder.setFirstName(thatPerson.getFirstName());
}

if (!nullToEmpty(thatPerson.hasLastName()).trim().isEmpty()) {
  thisPersonBuilder.setLastName(thatPerson.getLastName());
}

if (!nullToEmpty(thatPerson.hasAddress1()).trim().isEmpty()) {
  thisPersonBuilder.setAddress1(thatPerson.getAddress1());
}

Person thisPerson = thisPersonBuilder.build();