web-dev-qa-db-ja.com

python fileでgoogle.protobuf.Anyを使用する

私はそのような.protoファイルを持っています

syntax = "proto3";
import "google/protobuf/any.proto";

message Request {
  google.protobuf.Any request_parameters = 1;
}

Requestオブジェクトを作成し、そのフィールドにデータを入力するにはどうすればよいですか?私はこれを試しました:

import ma_pb2
from google.protobuf.any_pb2 import Any

parameters = {"a": 1, "b": 2}
Request = ma_pb2.Request()
some_any = Any()
some_any.CopyFrom(parameters)
Request.request_parameters = some_any

しかし、私にはエラーがあります:

TypeError: Parameter to CopyFrom() must be instance of same class: expected google.protobuf.Any got dict.

[〜#〜]更新[〜#〜]

@Kevinのプロンプトに従って、.protoファイルに新しいメッセージを追加しました。

message Small {
  string a = 1;
}

これで、コードは次のようになります。

Request = ma_pb2.Request()
small = ma_pb2.Small()
small.a = "1"

some_any = Any()
some_any.Pack(small)

Request.request_parameters = small

しかし、最後の割り当てでエラーが発生しました:

Request.request_parameters = small
AttributeError: Assignment not allowed to field "request_parameters" in protocol message object.

私は何を間違えましたか?

9

Anyは、任意のキーと値を格納するためのマジックボックスではありません。 Anyの目的は、実行時まで使用するメッセージがわからない場合に、 「任意の」メッセージタイプを示す にすることです。ただし、実行時には、特定のメッセージを念頭に置く必要があります。次に、 .Pack()および.Unpack()メソッドを使用 でそのメッセージをAnyに変換し、その時点であなたは Request.request_parameters.CopyFrom(some_any) のようなことをするでしょう。

したがって、この特定の辞書を保存する場合は、次のようにします。

{"a": 1, "b": 2}

... aおよびbという名前の整数フィールドを持つメッセージタイプを記述した.protoファイルが必要です。個人的には、それはやり過ぎだと思います。 aフィールドとbフィールドを分離する正当な理由がない限り、Requestメッセージに直接スローするだけです。これらのキーの1つを「忘れた」場合、 後でいつでも追加できます なので、完全性についてあまり心配する必要はありません。


上記で説明したものではなく、「任意のキーと値を格納するためのマジックボックス」が本当に必要な場合は、Anyの代わりに Map を使用できます。これには、キーのセットに任意の文字列(HTTPヘッダーなど)が含まれている可能性がある場合に、すべてのキーを事前に宣言する必要がないという利点があります。属性よりも文字列のスペルを間違えやすいため、リントや型チェックが難しいという欠点があります(特に静的に型付けされた言語で)。リンクされたリソースに示されているように、マップは基本的に次のような繰り返しフィールドの構文糖衣です(つまり、オンワイヤ表現はこれを行うことで得られるものとまったく同じであるため、そうでないクライアントとの下位互換性がありますマップをサポートしていません):

message MapFieldEntry {
  key_type key = 1;
  value_type value = 2;
}

repeated MapFieldEntry map_field = N;
9
Kevin