djangoprotocol-buffersgrpcgrpc-python

Converting django serializer data to google.protobuf.struct_pb2.Struct gives error: TypeError: bad argument type for built-in operation


I want to create a response message in grpc with the following format:

message Match {
  int32 index = 1;
  google.protobuf.Struct match = 2;
  google.protobuf.Struct status = 3;
}

I have a serializer and I want to convert the data into protobuf struct. my serializer is nested and long, so i have posted only a small part of the code in here.

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)

Besides, I cannot find the source of what is really making this problem. I am getting this error from serializing:

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

I have tried debugging tools in pycharm and gone through some lines, but I do not seem to find the problem


Solution

  • While e.g. 0 (int) is a valid key for a Python dict:

    d = {
        0: {0: "not started"},
        1: {1: "finished"},
        2: {9: "live"},
    }
    print(d)
    

    It is not a valid Struct key nor a valid JSON (object) key which Struct is designed to represent:

    match = Struct()
    match.update(d)
    

    Throws bad argument type for built-in operation

    You will need to use string values for keys:

    Struct:

    map<string, Value>