将Django序列化器数据转google.protobuf.Struct时遇TypeError错误
问题描述
需要创建如下gRPC响应消息:
message Match { int32 index = 1; google.protobuf.Struct match = 2; google.protobuf.Struct status = 3; }
使用Django嵌套序列化器将数据转换为protobuf Struct时触发类型错误,序列化器代码如下:
class MatchSerializer(proto_serializers.ModelProtoSerializer): competition = serializers.SerializerMethodField() match = serializers.SerializerMethodField() status = serializers.SerializerMethodField() class Meta: model = Match proto_class = base_pb2.Match fields = ['index', 'match', 'competition', 'status'] def get_competition(self, obj): data = SomeSerializer(obj.competition).data struct_data = struct_pb2.Struct() return struct_data.update(data) def get_status(self, obj): status_dict = { 0: {0: "not started"}, 1: {1: "finished"}, 2: {9: "live"}, } result = status_dict[obj.status] struct_data = struct_pb2.Struct() return struct_data.update(result) def get_match(self, obj): data = SomeOtherSerializer(obj.match).data struct_data = struct_pb2.Struct() return struct_data.update(data)
错误栈信息:
Traceback (most recent call last): File "/project/venv/lib/python3.10/site-packages/grpc/_server.py", line 555, in _call_behavior response_or_iterator = behavior(argument, context) File "/project/base/grpc/services.py", line 44, in GetUserSubscribedTeamsMatches return MatchSerializer(val).message File "/project/venv/lib/python3.10/site-packages/django_grpc_framework/proto_serializers.py", line 31, in message self._message = self.data_to_message(self.data) File "/project/venv/lib/python3.10/site-packages/rest_framework/serializers.py", line 555, in data ret = super().data File "/project/venv/lib/python3.10/site-packages/rest_framework/serializers.py", line 253, in data self._data = self.to_representation(self.instance) File "/project/venv/lib/python3.10/site-packages/rest_framework/serializers.py", line 522, in to_representation ret[field.field_name] = field.to_representation(attribute) File "/project/venv/lib/python3.10/site-packages/rest_framework/fields.py", line 1838, in to_representation return method(value) File "/project/base/grpc/serializers.py", line 97, in get_matches return struct_data.update(data[0]) File "/project/venv/lib/python3.10/site-packages/google/protobuf/internal/well_known_types.py", line 522, in update _SetStructValue(self.fields[key], value) File "/project/venv/lib/python3.10/site-packages/google/protobuf/internal/well_known_types.py", line 448, in _SetStructValue struct_value.struct_value.update(value) File "/project/venv/lib/python3.10/site-packages/google/protobuf/internal/well_known_types.py", line 522, in update _SetStructValue(self.fields[key], value) TypeError: bad argument type for built-in operation
解决方法
1. 修正update()方法的返回逻辑
struct_pb2.Struct().update()是原地修改结构体的方法,返回值为None,但序列化器需要返回Struct对象本身。正确写法是先执行更新,再返回结构体:
def get_competition(self, obj): data = SomeSerializer(obj.competition).data struct_data = struct_pb2.Struct() struct_data.update(data) return struct_data
2. 确保字典键为字符串类型
Protobuf的Struct要求所有键必须是字符串,你在status_dict中使用了整数作为内层键,会触发类型错误。需要将键转换为字符串:
def get_status(self, obj): status_dict = { 0: {"0": "not started"}, 1: {"1": "finished"}, 2: {"9": "live"}, } result = status_dict[obj.status] struct_data = struct_pb2.Struct() struct_data.update(result) return struct_data
3. 转换序列化器数据为兼容类型
Django序列化器返回的data可能包含Protobuf Struct不支持的类型(如Decimal、datetime对象),需要提前转换:
from decimal import Decimal from datetime import datetime def convert_to_struct_compatible(data): if isinstance(data, dict): return {str(k): convert_to_struct_compatible(v) for k, v in data.items()} elif isinstance(data, list): return [convert_to_struct_compatible(item) for item in data] elif isinstance(data, Decimal): return float(data) # 或转为字符串,根据业务需求选择 elif isinstance(data, datetime): return data.isoformat() else: return data
在序列化器中使用该转换函数:
def get_match(self, obj): data = SomeOtherSerializer(obj.match).data compatible_data = convert_to_struct_compatible(data) struct_data = struct_pb2.Struct() struct_data.update(compatible_data) return struct_data
4. 排查get_matches方法的异常
错误栈中提到get_matches方法调用struct_data.update(data[0]),但你提供的代码中没有这个方法。需要确认data[0]是否为有效的字典类型,避免传递非字典数据给update()方法。
内容的提问来源于stack exchange,提问作者Mahsa Fathi
相关产品推荐
相关产品推荐

