Как замаскировать определенные поля в Protobuf

Я не мог найти способ замаскировать определенные поля в структуре protobuf. Я читал о FieldMaskUtil и пробовал несколько примеров, но, похоже, все наоборот, то есть поля копирования, которые упоминаются в FieldMask, что отличается от того, что я хотел. Вот пример структуры и соответствующий тестовый код.

Прото:

syntax = "proto3";

package model;

option java_package = "test.demo.services.protobuf.customer.model";
option java_outer_classname = "CustomerProto";

message Accounts {
  repeated Account account = 1;
}

message Account {

  int32 id = 1;
  string number = 2;
  int32 customer_id = 3;

}

 message Customers {
   repeated Customer customers = 1;
}

message Customer {

  int32 id = 1;
  string pesel = 2;
  string name = 3;
  CustomerType type = 4;
  repeated Account accounts = 5;

  enum CustomerType {
    INDIVIDUAL = 0;
    COMPANY = 1;
  }

}

Вот пример тестового кода

package test.demo.services.protobuf.customer.model;

import org.junit.Test;
import test.demo.services.protobuf.customer.model.CustomerProto.Customer;
import com.google.protobuf.util.FieldMaskUtil;


public class TestMerge {


  @Test
  public void eraseFields() {

        Customer request = Customer.newBuilder().setId(10).setPesel("12345").setName("Harry Alto").build();
    // Erase name
      Customer.Builder modifieldRequest = Customer.newBuilder();
      FieldMaskUtil.merge(FieldMaskUtil.fromString("name"), request, modifieldRequest);
      System.out.println( modifieldRequest.build().toString());
}

}

Вот вывод:

name: "Harry Alto"

То, что я ожидал, это напечатать все, кроме имени

id: 10
pesel: "12345"

Есть ли способ сделать то, что я хочу

2 ответа

Решение
FieldMaskUtil.merge(FieldMaskUtil.fromString("name"), request, modifieldRequest);

То, что я ожидал, это напечатать все, кроме имени

Нет, согласно JavaDocs для FieldMask, поведение противоположно тому, что вы описали:

Маски полей используются для указания подмножества полей, которые должны быть возвращены операцией get или изменены операцией обновления.

Маска действует как заданная операция пересечения, выбирая только те поля, которые были указаны. В вашем случае маска указывает только "имя", так что это то, что она выбирает.

Требуемое поведение на самом деле является операцией дополнения множеством, выбирая все поля, которые не были указаны. Я не знаю встроенного метода в API Protobuf, чтобы сделать это. Варианты реализации этого самостоятельно:

  • Построить несколько FieldMask экземпляры, использующие отдельный вызов FieldMask#fromString для каждого именованного поля, которое вы хотите сохранить. Затем используйте FieldMaskUtil#union объединить их всех. Это сохранит только специально перечисленные поля. Если вы позже разовьете схему, добавив больше полей, она не получит эти новые поля. Вам придется изменить код и добавить новые именованные поля в этот союз.
  • Вызов MessageOrBuilder#getAllFieldsчтобы получить дескриптор для всех полей в сообщении. Переберите их всех, используя FieldMaskUtil#union как описано выше, но пропустите определенные поля, которые вы хотите игнорировать, например, "имя". Если вы позже разовьете схему, чтобы добавить больше полей, то эти новые поля будут выбраны. Вам не нужно менять код, чтобы начать включать эти новые поля.

Обратите внимание на различия в отношении эволюции схемы в этих решениях. Выбор наиболее подходящего зависит от ваших требований.

Если вы действительно хотите стереть поле имени, вы должны сделать это следующим образом:

Customer.Builder request Customer.newBuilder()
    .setId(10).setPesel("12345").setName("Harry Alto").build();
// Erase name
Customer.Builder modifiedRequest = request.toBuilder();
Customer modifyRequest = Customer.getDefaultInstance();
FieldMaskUtil.merge(FieldMaskUtil.fromString("name"), modifyRequest, modifiedRequest);
System.out.println(modifiedRequest.build().toString());

Это должно дать желаемый результат.

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