#python #protocol-buffers
Вопрос:
У меня есть три файла .proto:
syntax = "proto3";
package first;
message Message {
string a1 = 1;
bool b1 = 2;
}
syntax = "proto3";
package second;
message Message {
string a2 = 1;
}
syntax = "proto3";
import "first.proto";
import "second.proto";
package my_oneof;
message Message {
oneof test {
first.Message first = 1;
second.Message second = 2;
}
}
Насколько я понимаю, это должно сработать, но когда я пытаюсь это сделать:
from google.protobuf.json_format import ParseDict
import first_pb2 as First
import my_oneof_pb2 as MyOneOf
if __name__ == '__main__':
json = {"a1": "testtest123", "b1": True}
m = ParseDict(json, First.Message())
x = MyOneOf.Message()
x.ParseFromString(m.SerializeToString())
print(x)
Я получаю ошибку:
Traceback (most recent call last):
File (hidden), line 10, in <module>
x.ParseFromString(m.SerializeToString())
google.protobuf.message.DecodeError: Error parsing message
Теперь самое странное, что когда я немного изменяю номерные теги в oneof, это работает. В этом случае, если я начну с первого поля, 2
оно завершится без ошибок, вот так:
message Message {
oneof test {
first.Message first = 2;
second.Message second = 3;
}
}
Каково определение тегов внутри oneof? Если это oneof, и теги означают, где данные расположены в байтах, не должны ли все поля быть =1
такими, как только одно из них в любом случае является одним в каждом вхождении? Почему это работает с =2
, но не с =1
тем, о чем я упоминал? Я использую protobuf 3.17.3 как для библиотеки python, так и для компилятора.
Заранее спасибо!
Ответ №1:
Теги не имеют никакого отношения к тому, где расположены байты. oneof
элементы работают точно так же, как optional
поля, но:
- библиотека гарантирует, что только одному из них (не более) может быть присвоено значение одновременно
- сгенерированный код может содержать дополнительные фрагменты, чтобы определить, какой из них назначен
- сгенерированный код может (но не обязательно) оптимизировать внутренние компоненты, чтобы использовать тот факт, что назначен только один
Таким образом, сообщение с oneof представляет собой слой оболочки вокруг содержимого. Вы не можете просто написать одно из внутренних сообщений, а затем прочитать его, как если бы это было внешнее сообщение (то, которое содержит oneof) — на самом деле вам нужно будет вручную создать экземпляр внешнего сообщения, присвоив соответствующее поле oneof, и записать его.
Комментарии:
1. Спасибо за ответ. Не вопрос, но я буду использовать свои знания себе на пользу в любом случае, если вы можете — вы знаете, любым способом, то чтобы получить сериализации protobuf объекта (в байтах), схемы которых я не знаю (может быть
first
илиsecond
в нашем случае), но я знаю, что это один из некоторых вариантах, определиться во время выполнения какой схеме, чтобы попытаться десериализовать его? (Это то, что, как я думалoneof
, делает)2. @user3804691 как
oneof
работает путем добавления программу-оболочку с полем заголовка, который говорит, какой поля (по номеру) она; вот как это работает; так вот: нет, не волшебный способ, зная, что это не то; вы можете проверить, какие поля содержит, но это тяжело реализации (язык и т. п.) конкретной