正在从C#中读取protobuf3自定义选项
本文关键字:protobuf3 自定义 选项 读取 | 更新日期: 2023-09-27 18:03:39
TL;DR
根据文档,如果我使用C++,我可以使用string value = MyMessage::descriptor()->options().GetExtension(my_option);
读取自定义选项的值。Java和Python也有类似的例子。但我正在做C#,我可以找到一个等价的。我能做吗?如果可以,怎么做?
更多详细信息
我正在操作使用protobuf3生成的类。架构正在声明一个自定义选项。它看起来像这样:
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原生支持的,我现在使用谷歌的protobuf3 C#库。
您现在可以访问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的类。从中,您可以找到扩展名,然后找到字段号。这里有一个例子,假设proto被称为"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"
}
看起来该功能尚未实现:https://github.com/google/protobuf/issues/1603
看起来这只是时间问题,他们对拉取请求持开放态度。因此,根据您需要它的时间,您可以是执行该实现的人:(
更新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等…(
读取自定义消息选项(扩展(
- 假设文件名为
foobar.proto
message.Descriptor.GetOptions().GetExtension(FoobarExtensions.MyOption);
访问描述符数据的简单版本:
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
}
}