Gardes
Gardes

Reputation: 125

How to read EnumValueOptions from Protobuf in C#?

I want to read fields of type "EnumValueOptions". All examples I have found are related to languages other than "C#".

There is a protocol:

syntax = "proto3";   
import "google/protobuf/descriptor.proto"; 

extend google.protobuf.EnumValueOptions {
   string enum_name = 51234;
};;

message Options {
  enum Profiles{
        A = 0 [(enum_name) = "AAA"];
        B = 1 [(enum_name) = "BBB"]; 
        C = 2 [(enum_name) = "CCC"];
  };
  Profiles profile = 1;
}

How to read "enum_name" in code C#?

expecting:

foreach(Profiles profile in Enum.GetValues(typeof(Profiles)))
{
    string name = ????; //name in ("AAA","BBB","CCC")
}

Upvotes: 1

Views: 2654

Answers (3)

kibblewhite
kibblewhite

Reputation: 169

I'm programming in C# - still pretty new to this protobuf stuff

If you have the following proto file extending the EnumValueOptions:

extend google.protobuf.EnumValueOptions {
  string Value = 101;
  bool AutoEnrol = 102;
}

Protobuf will produce a EnumValueOptionsExtensions class through auto-generation. To access it use EnumValueOptionsExtensions.Value or EnumValueOptionsExtensions.AutoEnrol, per the C# code sample a little later/below.

Then in the same or another proto file, create an enum with the following:

enum SystemRoleType {
  ReservedRole = 0 [(Value) = "SystemRoleType.ReservedRole", (AutoEnrol) = false];
  Administrator = 1001 [(Value) = "SystemRoleType.Administrator", (AutoEnrol) = false];
  Editor = 1002 [(Value) = "SystemRoleType.Editor", (AutoEnrol) = false];
  ContentCreator = 1003 [(Value) = "SystemRoleType.ContentCreator", (AutoEnrol) = false];
  User = 1004 [(Value) = "SystemRoleType.ContentCreator", (AutoEnrol) = true];
}

Again, this will produce a SystemRoleTypeReflection class through protobuf auto-generation.

Then in C#, include your namespaces accordingly (that also includes: Google.Protobuf.Reflection) and then you could do something like the following in C#

EnumDescriptor SystemRoleTypeTypeEnumDescriptor = SystemRoleTypeReflection.Descriptor.FindTypeByName<EnumDescriptor>(typeof(SystemRoleType).Name);
foreach (SystemRoleType system_role_type in Enum.GetValues(typeof(SystemRoleType)))
{
    EnumValueDescriptor enum_value_descriptor = SystemRoleTypeTypeEnumDescriptor.FindValueByNumber((int)system_role_type);
    var selector_value = enum_value_descriptor.GetOptions().GetExtension<string>(EnumValueOptionsExtensions.Value);
    var auto_enrolment = enum_value_descriptor.GetOptions().GetExtension<bool>(EnumValueOptionsExtensions.AutoEnrol);
}

Updated (2021-12-01): I put a tiny VS solution together today, that you can open in Visual Studio Community 2022 (.Net 6 / C# 10) and run the example in a console app: github.com/kibblewhite/EnumValueOptions-gRPC-Example

Upvotes: 2

subclassed
subclassed

Reputation: 21

Proto:

syntax = "proto2";

package my.protos;
option csharp_namespace = "My.Protos";

import "google/protobuf/descriptor.proto"; // For enum extension.

extend google.protobuf.EnumValueOptions {
    optional string NameDisplay = 50001;
    optional string NameAbbrev  = 50002;
}

enum MyOperation {
    MY_CREATE = 0 [(NameDisplay) = "create"];
    MY_DELETE = 1 [(NameDisplay) = "delete"];
    MY_READ   = 2 [(NameDisplay) = "read"];
}

Usage in C#:

Google.Protobuf.Reflection.EnumDescriptor descriptor = 
    MyProtosReflection.Descriptor.FindTypeByName<Google.Protobuf.Reflection.EnumDescriptor>(
        typeof(MyOperation).Name
    );

Google.Protobuf.Reflection.EnumValueDescriptor valueDescriptor =
    descriptor.FindValueByNumber((int)MyOperation.Create);

string nameDisplay = valueDescriptor.GetOption(MyProtosExtensions.NameDisplay);

Upvotes: 1

shingo
shingo

Reputation: 27404

Protobuf doesn't generate c# codes for enum value options, so if you want to retrieve these values, you need edit the source at src/google/protobuf/compiler/csharp/csharp_doc_comment.cc.

Here is an example

void WriteEnumValueDocComment(io::Printer* printer, const EnumValueDescriptor* value) 
{
    WriteDocCommentBody(printer, value);
    
    // Example code to prepend c# attribute like '[Custom(name: value)]' before a enum field
    const Message& options = value->options();
    const Reflection* reflection = options.GetReflection();
    std::vector<const FieldDescriptor*> fields;
    reflection->ListFields(options, &fields);
    for (int i = 0; i < fields.size(); i++)
    {
        std::string name = fields[i]->full_name();
        std::string fieldval;
        TextFormat::PrintFieldValueToString(options, fields[i], -1, &fieldval);
        printer->Print("[Custom($name$: $value$)]", "name", name, "value", fieldval);
    }
}

Regenerate cs file then you can use GetCustomAttribute method to retrieve these information.

Upvotes: 1

Related Questions