Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading a protobuf3 custom option from C#

TL;DR

According to the doc, if I were doing C++, I could read the value of a custom option using string value = MyMessage::descriptor()->options().GetExtension(my_option);. There are similar examples for Java and Python. But I'm doing C# and I could find an equivalent. Can I do it, and if yes, how?

More details

I'm manipulating classes generated with protobuf3. The schemas are declaring a custom option. It looks like this:

import "google/protobuf/descriptor.proto";

extend google.protobuf.MessageOptions {
  string my_option = 51234;
}

message MyMessage {
  option (my_option) = "Hello world!";
}

My code is being provided an object generated from MyMessage, and I'd like to read the value of this option (here Hello world!)


Update: I'm not using protobuf-net. Now that C# is natively supported by protobuf, I'm using Google's protobuf3 C# library.

like image 606
gturri Avatar asked Aug 24 '16 07:08

gturri


People also ask

How do I open a .proto file?

You need a suitable software like Protobuf to open a PROTO file. Without proper software you will receive a Windows message "How do you want to open this file?" or "Windows cannot open this file" or a similar Mac/iPhone/Android alert.

What is Protobuf C#?

Protobuf is used to serialize data structure efficiently. It is faster, smaller & simpler than XML. Protobuf is useful in developing programs to communicate with each other over a wire and it is created by Google. It helps us in creating gRPC services.

What is the difference between Proto2 and Proto3?

Proto3 is the latest version of Protocol Buffers and includes the following changes from proto2: Field presence, also known as hasField , is removed by default for primitive fields. An unset primitive field has a language-defined default value.


1 Answers

You can now access custom options in C#. First, define the custom option in your .proto:

import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions {
  string objectReferenceType = 1000; //Custom options are 1000 and up.
}

Next, apply the custom option to something. Here I attached it to a field:

message Item
{
  string name = 1;
  int32 id = 2;
  string email = 3;
  ObjectReference prefab = 4 [(objectReferenceType) = "UnityEngine.GameObject"];
}

Then you need to lookup the custom option field number. There's no nice way to do this, so just look up the extension from FileDescriptor of the file where you defined the custom option extension. You will have a C# generated class called protoFileNameReflection. From that, you can find the extension then the field number. Here's an example assuming the proto is called "Item.proto" so the generated class is called ItemReflection:

foreach (FieldDescriptor extensionFieldDescriptor in ItemReflection.Descriptor.Extensions.UnorderedExtensions)
    {   
        if (extensionFieldDescriptor.ExtendeeType.FullName == "google.protobuf.FieldOptions")
        {
            objectReferenceTypeFieldNumber = extensionFieldDescriptor.FieldNumber;
            break;
        }
    }

Then access the custom option in code using protobuf reflection:

FieldDescriptor fieldDescriptor = prefabFieldDescriptor;
CustomOptions customOptions = fieldDescriptor.CustomOptions;
if (customOptions.TryGetString(objectReferenceTypeFieldNumber, out string objectReferenceTypeText))
{
   Console.Log(objectReferenceTypeText); //logs: "UnityEngine.GameObject"
}
like image 68
DoomGoober Avatar answered Nov 15 '22 10:11

DoomGoober