簡介
protobuf_to_dict
和 protobuf3_to_dict
提供了 protobuf instance 和 dict 之間的轉換函數。
在有些服務裡,新的版本的 protocol 會用到新的 protobuf 的東西,例如:google.protobuf.Value
, 這時,protobuf_to_dict
會壞掉,所以會需要遷移到 protobuf3_to_dict
,但為了能讓舊的資料(protobuf_to_dict
生的)和新的 protobuf3_to_dict
相容,就需要做一些準備。
這篇不會提到 official protobuf python lib 的 json_format,因為的輸入輸出格式和 *protobuf_to_dict*
不相容,所以並沒有考慮遷移到 json_format。
protobuf_to_dict 無法運行的地方
google.protobuf.Value 裡面有一種欄位的類別是 Struct
message Struct {
// Unordered map of dynamically typed values.
map<string, Value> fields = 1;
}
protobuf_to_dict
處理 Struct 時會直接丟 Exception
from google.protobuf.struct_pb2 import Struct
from protobuf_to_dict import protobuf_to_dict
s = Struct()
s.update({"key": "value"})
obj = protobuf_to_dict(s)
# AttributeError` : 'unicode' object has no attribute 'ListFields'
protobuf3_to_dict
map 在 google 的定義上為了能夠向後相容,要求實作要接受一個 Repeated List (reference)。這在 protobuf3_to_dict 裡面做了特化處理,輸出的 json 會直接是 dict ,而不像一般 Repeated List 會輸出成 sequence。
if field.message_type and field.message_type.has_options and field.message_type.GetOptions().map_entry:
result_dict[field.name] = dict()
value_field = field.message_type.fields_by_name['value']
type_callable = _get_field_value_adaptor(
pb, value_field, type_callable_map,
use_enum_labels, including_default_value_fields,
lowercase_enum_lables)
for k, v in value.items():
result_dict[field.name][k] = type_callable(v)
continue
protobuf_to_dict 和 protobuf3_to_dict 不同的地方: Bytes 欄位
protobuf_to_dict 會先把 bytes 欄位 encode 成 base64 ,然後再進行處理, protobuf3_to_dict 則不會。
這造成 protobuf_to_dict 要把 dict 轉回 protobuf 時,會需要對 bytes 欄位進行 base64 decode。
假如這時要用 protobuf3_to_dic.dict_to_protobuf
套用到舊資料,或者讓 protobuf3_to_dict.protobuf_to_dict
生成的新資料和舊資料形式一樣,就會需要多傳一個 map 進去。
# 舊版本用到的轉換函數
TYPE_CALLABLE_MAP = {
FieldDescriptor.TYPE_DOUBLE: float,
FieldDescriptor.TYPE_FLOAT: float,
FieldDescriptor.TYPE_INT32: int,
FieldDescriptor.TYPE_INT64: long,
FieldDescriptor.TYPE_UINT32: int,
FieldDescriptor.TYPE_UINT64: long,
FieldDescriptor.TYPE_SINT32: int,
FieldDescriptor.TYPE_SINT64: long,
FieldDescriptor.TYPE_FIXED32: int,
FieldDescriptor.TYPE_FIXED64: long,
FieldDescriptor.TYPE_SFIXED32: int,
FieldDescriptor.TYPE_SFIXED64: long,
FieldDescriptor.TYPE_BOOL: bool,
FieldDescriptor.TYPE_STRING: unicode,
FieldDescriptor.TYPE_BYTES: lambda b: b.encode("base64"),
FieldDescriptor.TYPE_ENUM: int,
}
REVERSE_TYPE_CALLABLE_MAP = {
FieldDescriptor.TYPE_BYTES: lambda b: b.decode("base64"),
}
只要把 TYPE_CALLABLE_MAP
傳進去 protobuf_to_dict
或 REVERSE_TYPE_CALLABLE_MAP
傳進 dict_to_protobuf
即可。
protobuf_to_dict(pb, type_callable_map=TYPE_CALLABLE_MAP)
dict_to_protobuf(pb, my_dict, type_callable_map=REVERSE_TYPE_CALLABLE_MAP)