twentythousand
twentythousand

Reputation: 41

Can you assign multiple different value types to one field in a repeated Protobuf message?

I'm trying to reverse engineer a client that uploads an audio file to a server, and then uploads the file's metadata in a separate request afterwards. The metadata is serialized in Protobuf, and it uses a fairly simple and readable structure. Here's what it looks like after protoc --decode_raw:

1 {
  1: "title"
  2: "This is the audio title"
}
1 {
  1: "tags"
  2 {
  }
}
1 {
  1: "loc"
  2: "This is the location"
}
1 {
  1: "transcription"
  2: "A transcript"
}
1 {
  1: "mapping"
  2 {
    1 {
      1: 6
      2 {
        3: 840
      }
    }
    2: 6
  }
}

It seems to be just a repeated message at field 1 that holds a key-value pair each time, but sometimes the value is a string, and sometimes it's a more complex message. How can they assign both a string and a message to field 2 if Protobuf only lets you use one value type per field? If I'm going to craft my own request, I need something like this:

message KeyValuePair {
    string key = 1;
    oneof value {
        string str_value = 2;
        MessageValue msg_value = 2;
    }
}

But that doesn't work, because Field number 2 has already been used in "package.name" by field "str_value". Any ideas? I'll be using Python to create and send the request data.

Upvotes: 3

Views: 4525

Answers (1)

Peter Wishart
Peter Wishart

Reputation: 12270

There is an official way to achieve this: google.protobuf.Any

If a protobuf schema defines an any at the top level, e.g.:

message Root {
  repeated google.protobuf.Any value = 1;
}

message Title {
  string title= 2;
}

message Tags {
  string name = 1;
  repeated string tags = 2;
}

Then messages of any Protobuf-defined type can be serialized in a list.

However, I don't think that's what the existing code is doing:

  • the raw output for Any normally includes the type.googleapis.com type url
  • Using Any, the title / loc fields would be encapsulated in a nested object, not a string at the same level.

E.g.:

1 {
  1: "type.googleapis.com/Title"
  2 {
    1: "the title"
  }
}

Upvotes: 1

Related Questions