简体   繁体   English

从 C# 读取一个 protobuf3 自定义选项

[英]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);根据文档,如果我正在执行 C++,我可以使用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. Java 和 Python 有类似的例子。但我正在做 C#,我可以找到一个等价物。 Can I do it, and if yes, how?我可以做吗?如果可以,怎么做?

More details更多细节

I'm manipulating classes generated with protobuf3 .我正在处理使用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! )我的代码被提供了一个从MyMessage生成的 object,我想阅读这个选项的值(这里是Hello world!


Update: I'm not using protobuf.net.更新:我没有使用 protobuf.net。 Now that C# is natively supported by protobuf, I'm using Google's protobuf3 C# library.现在 C# 已经被 protobuf 原生支持了,我正在使用谷歌的 protobuf3 C# 库。

You can now access custom options in C#.您现在可以在 C# 中访问自定义选项。 First, define the custom option in your .proto:首先,在你的 .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.没有好的方法可以做到这一点,因此只需从您定义自定义选项扩展名的文件的 FileDescriptor 中查找扩展名。 You will have a C# generated class called protoFileNameReflection.您将拥有一个 C# 生成的类,称为 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:这是一个假设 proto 名为“Item.proto”的示例,因此生成的类称为 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:然后使用 protobuf 反射访问代码中的自定义选项:

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

Looks like the feature hasn't been implemented yet: https://github.com/google/protobuf/issues/1603看起来这个功能还没有实现: https : //github.com/google/protobuf/issues/1603

It also looks like it's only a matter of time and they're open to pull requests.看起来这只是时间问题,他们对拉取请求持开放态度。 So depending on how soon you need it, you could be the one doing the implementation :)因此,根据您需要它的时间,您可能是执行此操作的人:)

Updating the answer for Protobuf 3.11.4 as this is the only thread dealing with the problem.更新 Protobuf 3.11.4 的答案,因为这是处理该问题的唯一线程。 Using similar protos as DoomGoober:使用与 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"];
}

You can read the custom options from an Item Proto object using a new generated class.您可以使用新生成的类从 Item Proto 对象读取自定义选项。 In this case its called FooExtensions (see Foo.protos):在这种情况下,它称为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";
  }
}

You are able to fetch all types of options in the same way (MessageOptions, FileOptions).您可以以相同的方式获取所有类型的选项(MessageOptions、FileOptions)。 Simply ensure you are using the correct descriptor (For MessageOptions use MessageDescriptors and so on...)只需确保您使用正确的描述符(对于 MessageOptions 使用 MessageDescriptors 等...)

Reading Custom Message Options (Extensions)阅读自定义消息选项(扩展)

  • Assuming the file is called foobar.proto假设文件名为foobar.proto

message.Descriptor.GetOptions().GetExtension(FoobarExtensions.MyOption);

A simple version to access the descriptor data:访问描述符数据的简单版本:

public void LogFieldOptions(Item item)
{
   var fieldDescriptors = item.Descriptor.Fields.InDeclarationOrder(); // or .InFieldNumberOrder()
   var fieldDescriptor = fieldDescriptors.FirstOrDefault(fd => fd.Name == "prefab")

   if (fieldDescriptor != null)
   {
       var objectReferenceType = fieldDescriptor.GetOptions().GetExtension(FooExtensions.objectReferenceType); 
       // use result
   }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM