Search code examples
protocol-buffers

Message type "PrettyList" has no field named "driver_activity_v1_2". Available Fields(except extensions): ['entities']


I want to handle messages like this in protobuf:

Python code:

request_proto = PrettyRequest()
Parse(body, request_proto)

I have an error:

Message type "some_path.PrettyList" has no field named "driver_activity_v1_2". Available Fields(except extensions): ['entities']

How to fix protobuf structure?

Json-body example

{
    "my_services": {
        "service1": {
            "entities": {
                "driver_id":[1001, 1003]
            }
        },
        "serviceN": {
            "entities": {
                "driver_id":[1, 2]
            }
        }
    }
}

My proto file:

message PrettyRequest {
    PrettyList my_services = 1;
}

message PrettyList {
    map<string, EntitiesDict> entities = 1;
}

message EntitiesDict {
    map<string, RepeatedValue> entities = 1;
}

message RepeatedValue {
  repeated Value val = 1;
}

message Value {
  // ValueType is referenced by the metadata types, FeatureInfo and EntityInfo.
  // The enum values do not have to match the oneof val field ids, but they should.
  // In JSON "*_val" field can be omitted
  oneof val {
    bytes bytes_val = 1;
    string string_val = 2;
    int32 int32_val = 3;
  }
}


Solution

  • There are at least 2 ways that you can achieve this.

    Generally (!) using JSON as a template for a Protobuf schema is challenging because JSON is weakly typed whereas Protobuf messages are strongly-typed.

    Protobuf
    syntax="prpto3";
    
    message X {
      map<string,Y> my_services = 1;
    }
    message Y {
      repeated int32 driver_id = 1;
    }
    
    x = foo_pb2.X(
        my_services={
            "service1":foo_pb2.Y(
                driver_id=[1001,1003]
            ),
            "serviceN":foo_pb2.Y(
                driver_id=[1,2]
            )
        }
    )
    json_string = json_format.MessageToJson(x,preserving_proto_field_name=True)
    print(json_string)
    

    NOTE I've used X and Y as placeholders.

    Yields:

    {
      "my_services": {
        "serviceN": {
          "driver_id": [
            1,
            2
          ]
        },
        "service1": {
          "driver_id": [
            1001,
            1003
          ]
        }
      }
    }
    

    NOTE This matches your JSON sample.

    Well-known Type 'Value'

    Google Well-known Types include Value which can be used to represent arbitrary (any type) JSON objects.

    from google.protobuf import json_format
    from google.protobuf.struct_pb2 import Value
    
    v = Value()
    
    j = '''{
        "my_services": {
            "service1": {
                "entities": {
                    "driver_id":[1001, 1003]
                }
            },
            "serviceN": {
                "entities": {
                    "driver_id":[1, 2]
                }
            }
        }
    }'''
    
    # print(j)
    
    json_format.Parse(j,v)
    # print(v)
    
    json_string = json_format.MessageToJson(v)
    print(json_string)
    

    Yields:

    {
      "my_services": {
        "serviceN": {
          "entities": {
            "driver_id": [
              1.0,
              2.0
            ]
          }
        },
        "service1": {
          "entities": {
            "driver_id": [
              1001.0,
              1003.0
            ]
          }
        }
      }
    }
    

    NOTE This matches your JSON sample.

    The main challenges with using Value are that (a) the Protobuf message structure is complicated; (b) the Protobuf message structure accepts any JSON not just messages from types inferred from your JSON sample.