我正在尝试在给定 typeURL 的 Go 中生成原型实例。基本上我想做的是模式验证。我有一条原始消息发送到 gRPC 服务,该消息有一个字段,即 protobuf.Any。现在“Any”包含我使用 option go_package 指定的“typeUrl”(并且我已经验证它是正确的),以及“Value”,它是包含我需要的数据的编组原型的字节数组。
我想做的是,当我在服务器端收到请求时,我想使用“typeUrl”生成“Value”中包含的原型的空实例。下面是我的代码:
func NewProtoInstanceFromAny(any *anypb.Any) (protoreflect.ProtoMessage, error) {
typeURL := any.TypeUrl
// Parse the TypeURL to get the package name and message name
segments := strings.Split(typeURL, "/")
packageName := segments[0]
messageName := segments[1]
println(messageName)
println(packageName)
messageType, _ := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(packageName + "." + messageName))
if messageType == nil {
print("YES")
return nil, fmt.Errorf("message %s not found in package %s", messageName, packageName)
}
newValue := messageType.New().Interface()
// Unmarshal the value field of the Any proto into the new message instance
println("HEY")
if err := protojson.Unmarshal(any.Value, newValue); err != nil {
return nil, err
}
print("STRING" + newValue.ProtoReflect().Descriptor().FullName())
问题是我的 messageType 为 nil,这可能意味着在“GlobalRegistry”中找不到该文件。所以我想我的问题是,如何将原型添加到注册表中?我正在玩 protoregistry.Files{} 但似乎无法弄清楚。这是我在网上找到的一个例子,但它不适用于我的情况:
protoFile, err := ioutil.ReadFile("path/to/file/test.proto")
if err != nil {
print(err)
}
pbSet := new(descriptorpb.FileDescriptorSet)
if err := proto.Unmarshal(protoFile, pbSet); err != nil {
print(err)
}
pb := pbSet.GetFile()
// Initialize the file descriptor object.
fd, err := protodesc.NewFile(pb[0], protoregistry.GlobalFiles)
if err != nil {
print(err)
}
// and finally register it.
err = protoregistry.GlobalFiles.RegisterFile(fd)
if err != nil {
print(err)
}
由于某种原因,此代码因索引超出范围而发生恐慌。希望得到一些帮助!
解决方案
您需要导入包含生成的
.pb.go
文件的包。因此,如果原始文件是 path/to/foo.proto
并且生成的文件是 path/to/foo/foo.pb.go
,那么一个简单的导入语句就足够了。
import _ "path/to/foo"
原因
消息在生成文件的
init
函数中注册到全局注册表中。 init
函数创建一个类型生成器并调用其 Build
方法。在此函数中,有点复杂,但您可以看到类型及其描述符已在全局注册表中注册。这是相关的源代码。