Как бороться с неизвестными полями protobuf в Java?

У меня есть приложение Java, которое считывает некоторые данные protobuf с другого компьютера, а затем может изменять некоторые значения и записывать их обратно. Очень вероятно, что пользователь может прочитать данные, используя устаревший файл .proto, поэтому в этом случае будут некоторые поля, которые он не понимает. В конечном итоге я хотел бы сохранить неизвестные данные при записи внесенных изменений; однако я мог бы просто обнаружить наличие неизвестных данных (чтобы предложить пользователю обновить свое приложение). Мне непонятно, как быть с неизвестными полями в Java.

Если это поможет, я использую файл .proto версии 2, потому что мне нужно, чтобы он был совместим с nanopb на удаленном компьютере.

Этот вопрос дает мне часть пути, но мой вопрос не имеет ничего общего с JSON.


person Mike Foss    schedule 11.05.2019    source источник
comment
Вы тестировали поведение, когда пользователь открывает данные, используя устаревший файл .proto? Есть ли простое исключение, которое вы можете поймать и отреагировать в этом случае?   -  person Richard Neish    schedule 13.05.2019
comment
если вы создаете класс Java с использованием протокола, он содержит поле unknownFields, он будет заполнен номером и значением тега (поскольку новый прототип содержит, например, тег 4, который недоступен в устаревшем файле прототипа)   -  person freakman    schedule 21.05.2019


Ответы (1)


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

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

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

Builder builder = foo.toBuilder();
FieldDescriptor field = builder.getDescriptorForType().findFieldByName("whatever field");
Object obj = builder.getField(field);

// if your field is int32 cast to int
int value = (int) obj

Если вы хотите написать «неизвестное» значение, вы можете действовать наоборот:

Builder builder = foo.toBuilder();
FieldDescriptor field = builder.getDescriptorForType().findFieldByName("whatever field");
builder.setField(field, 100); // 100 is an example int value
Foo foo = builder.build();

В случае, если вы действительно хотите вставить прото-определенные неизвестные поля, вам нужно сделать что-то вроде:

 UnknownFieldSet.Field seqField = UnknownFieldSet.Field
            .newBuilder()
            .addFixed32(100) // 100 is an example int value
            .build();

 UnknownFieldSet unkFieldSet = UnknownFieldSet
            .newBuilder()
            .addField(99, seqField) // 99 is a proto index number chosen by me
            .build();

 Foo message = foo.toBuilder().setUnknownFields(unkFieldSet).build();

Чтение определенных неизвестных полей снова выполняется с помощью:

 foo.toBuilder().getUnknownFields()....

Надеюсь, это поможет.

person Antal Attila    schedule 11.02.2020