从C#读取protobuf3自定义选项

时间:2016-08-24 07:12:30

标签: c# protocol-buffers

TL; DR

根据文档,如果我在做C ++,我可以使用string value = MyMessage::descriptor()->options().GetExtension(my_option);读取自定义选项的值。 Java和Python也有类似的例子。但我正在做C#,我可以找到一个等价物。我可以这样做,如果是的话,怎么做?

更多详情

我操纵用protobuf3生成的类。模式声明custom option。它看起来像这样:

import "google/protobuf/descriptor.proto";

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

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

我的代码正在提供从MyMessage生成的对象,我想阅读此选项的值(此处Hello world!

更新:我没有使用protobuf-net。现在C#原生支持protobuf,我使用的是Google的protobuf3 C#库。

3 个答案:

答案 0 :(得分:1)

看起来该功能尚未实施:https://github.com/google/protobuf/issues/1603

看起来这只是时间问题而且他们愿意接受请求。因此,根据您需要的时间,您可以成为实施者:)

答案 1 :(得分:1)

您现在可以在C#中访问自定义选项。首先,在.proto中定义自定义选项:

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

接下来,将自定义选项应用于某些内容。在这里,我将其附加到一个字段上:

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

然后,您需要查找自定义选项字段号。没有很好的方法来执行此操作,因此只需从定义自定义选项扩展名的文件的FileDescriptor中查找扩展名即可。您将拥有一个由C#生成的名为protoFileNameReflection的类。由此,您可以找到扩展名和字段号。这是一个示例,假设该原型称为“ Item.proto”,因此生成的类称为ItemReflection:

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

然后使用protobuf反射访问代码中的自定义选项:

FieldDescriptor fieldDescriptor = prefabFieldDescriptor;
CustomOptions customOptions = fieldDescriptor.CustomOptions;
if (customOptions.TryGetString(objectReferenceTypeFieldNumber, out string objectReferenceTypeText))
{
   Console.Log(objectReferenceTypeText); //logs: "UnityEngine.GameObject"
}

答案 2 :(得分:0)

更新Protobuf 3.11.4的答案,因为这是唯一处理该问题的线程。使用与DoomGoober类似的原型:

// Foo.proto
Package foo
import "google/protobuf/descriptor.proto";
extend google.protobuf.FieldOptions {
  string objectReferenceType = 1000; //Custom options are 1000 and up.
}
// Bar.proto
import "Foo.proto"
message Item
{
  string name = 1;
  int32 id = 2;
  string email = 3;
  ObjectReference prefab = 4 [(foo.objectReferenceType) = "UnityEngine.GameObject"];
}

您可以使用新生成的类从Item Proto对象读取自定义选项。在这种情况下,其名为 FooExtensions (请参阅Foo.protos):

public void LogFieldOptions(Item item)
{
  // Get the list of fields in the message (name, id, etc...)
  var fieldDescriptors = item.Descriptor.Fields.InFieldNumberOrder();

  foreach (var fieldDescriptor in fieldDescriptors)
  {
    // Fetch value of this item instance for current field
    var fieldValue = fieldDescriptor.Accessor.GetValue(item);

    // Fetch name of field
    var fieldName = fieldDescriptor.Name;

    // if we are not in the correct field: Skip    
    if(!fieldName.Equals("prefab")) continue;

    // Fetch the option set in this field in the proto
    // (note that this is not related to the instance 
    // of item but to the general item message descriptor)
    var optionObjectReferenceType = fieldDescriptor.GetOption(FooExtensions.objectReferenceType); 
    Console.Log(optionObjectReferenceType ); //logs: "UnityEngine.GameObject";
  }
}

您能够以相同的方式获取所有类型的选项(MessageOptions,FileOptions)。只需确保您使用的描述符正确(对于MessageOptions,请使用MessageDescriptors等)