1

Having this proto3 schema on a PHP client and Python server:

service GetAnimalData{
    rpc GetData (AnimalRequest) returns (AnimalData) {}
}

message AnimalRequest {
    OneOfAnimal TypeAnimal = 1;
}

message AnimalData {
    repeated int32 data = 1;
}

message OneOfAnimal {
    oneof animal_oneof {
        CAT cat = 1;
        DOG dog = 2;
    }
}

message CAT{
    int32 p = 1;
    int32 d = 2;
    int32 q = 3;
}

message DOG{
    int32 p = 1;
    int32 d = 2;
    int32 q = 3;
    int32 p2 = 4;
    int32 d2 = 5;
    int32 q2 = 6;
}

In oder to set up a request from the PHP client, I need to do:

  1. Create a new CAT
  2. Set OneOfAnimal as CAT
  3. Set AnimalRequest.TypeAnimal as OneOfAnimal

Is there a schema for proto3 where I can just set either a CAT or DOG object directly as my AnimalRequest.TypeAnimal

Lloen
  • 68
  • 4
  • 11

1 Answers1

4

In general terms; no, not really.

In some very specific cases, the answer is "yes" - for example, protobuf-net (a "code-first" protobuf implementation in .NET) happens to implement inheritance in exactly this way, so you could use:

[ProtoContract]
public class AnimalRequest {
    [ProtoMember(1)] public Animal Animal {get;set;}
}

[ProtoContract]
[ProtoInclude(1, typeof(Cat))]
[ProtoInclude(2, typeof(Dog))]
class Animal {} // acts identically to your OneOfAnimal message type

[ProtoContract]
class Cat {
    [ProtoMember(1)] public int P {get;set;}
    [ProtoMember(2)] public int D {get;set;}
    [ProtoMember(3)] public int Q {get;set;}
}

[ProtoContract]
class Dog {
    [ProtoMember(1)] public int P {get;set;}
    [ProtoMember(2)] public int D {get;set;}
    [ProtoMember(3)] public int Q {get;set;}
    [ProtoMember(4)] public int P2 {get;set;}
    [ProtoMember(5)] public int D2 {get;set;}
    [ProtoMember(6)] public int Q2 {get;set;}
}

and then you use:

var result = svc.GetData(
    new AnimalRequest { Animal = new Dog { P = 42, ... Q2 = 19 } }
);

and it would work exactly the same way - the same bytes would be sent; essentially, protobuf-net treats inheritance as oneof (at each level in the type hierarchy).

But: because .proto is intended to work in a very generic "least common denominator" way, .proto itself has no concept of inheritance, so: you can't do this from .proto or the Google libraries.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Oh okay, protobuf-net seems like the way I expected to do it. That's unfortunate, but anyway just an extra step needed to take with ```.proto```. Thanks a lot of your answer! – Lloen Jun 17 '20 at 08:27
  • Thanks for this. This works with interfaces in addition to base classes, just FYI for anyone researching this. I wanted to do `oneof` via code-first. – Chris Benard Apr 27 '22 at 21:30