2

My goal is exactly the same as stated in this issue on github:

how to read an existing .proto file and get a FileDescriptor from it

I cannot use the suggested "workaround", for 2 reasons:

  • I have "plain" .proto files, i.e.:
  • I do not want to invoke the protoc compiler as an external application.

According to Marc this is possible with protobuf-net library:

Without a compiled schema, you would need a runtime .proto parser. [...] protobuf-net includes one (protobuf-net.Reflection)

I found Parsers.cs

Thanks Marc, but how do I use/do this? Is this the right entry point? Is there a minimal working example somewhere?

user11398730
  • 49
  • 1
  • 11

1 Answers1

3
var set = new FileDescriptorSet();
set.Add("my.proto", true);
set.Process();

That's all you need; note that if you want to provide the actual contents (rather than having the library do the file access), there is an optional TextReader parameter. If you need imports:

set.AddImportPath(...);

Once you've called Process, the .Files should be populated along with the .MessageTypes of each file, etc.

For a more complete example:

var http = new HttpClient();
var proto = await http.GetStringAsync(
 "https://raw.githubusercontent.com/protocolbuffers/protobuf/master/examples/addressbook.proto");

var fds = new FileDescriptorSet();
fds.Add("addressbook.proto", true, new StringReader(proto));
fds.Process();
var errors = fds.GetErrors();
Console.WriteLine($"Errors: {errors.Length}");

foreach(var file in fds.Files)
{
    Console.WriteLine();
    Console.WriteLine(file.Name);

    foreach (var topLevelMessage in file.MessageTypes)
    {
        Console.WriteLine($"{topLevelMessage.Name} has {topLevelMessage.Fields.Count} fields");
    }
}

Which outputs:

addressbook.proto
Person has 5 fields
AddressBook has 1 fields

google/protobuf/timestamp.proto
Timestamp has 2 fields

Notice that you didn't have to provide timestamp.proto or an import path for it - the library embeds a number of the common imports, and makes them available automatically.

(each file is a FileDescriptorProto; the group of files in a logical parse operation is the FileDescriptorSet - which is the root object used from descriptor.proto; note that all of the objects in this graph are also protobuf serializable, if you need a compiled/binary schema)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • That clarifies a lot. I was fiddling around with `fileDescriptorProto.Parse(new StreamReader("addressbook.proto"), new List(), null);` meanwhile, which actually works for a single file. Given that none of the two file related parameters is explicitly optional, that signature was a bit confusing to me. Is `Parse()` intentionally public? – user11398730 Jun 26 '20 at 07:59
  • @user11398730 probably not, no; my bad - marked as hidden and obsolete, here: https://github.com/protobuf-net/protobuf-net/commit/7b7f835d653d1e9b29bfafb987d47245296675e5#diff-66411385b26de390f650e973dafe4ab2R839-R844 – Marc Gravell Jun 26 '20 at 08:41
  • I doesn't see .Add method in FileDescriptorSet class. Which assembly/dependency I should use? – Anton Jul 19 '23 at 22:30
  • @Anton protobuf-net.Reflection – Marc Gravell Jul 20 '23 at 06:30