Определение числовых тегов внутри oneof с вложенными сообщениями

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