如何使用github.com/jhump/protoreflect 解析一个字节数组的proto消息知道消息描述符

Ars*_*ene 1 go protocol-buffers

我有一个包含以下 protomessage 字节片段的文件。

syntax = "proto3";

package main;

message Address {
    string street = 1;
    string country = 2;
    string state = 3;
}
Run Code Online (Sandbox Code Playgroud)

我的消息类型描述如下:

syntax = "proto3";

package main;

message Address {
    string street = 1;
    string country = 2;
    string state = 3;
}
Run Code Online (Sandbox Code Playgroud)

我想知道如何最好地使用jhump/protoreflect使用上面的消息描述符来解析文件的内容。

感谢您的帮助。

小智 5

典型的方法是使用以下命令将协议编译为 Go 代码protoc

protoc main.proto --go_out=.
Run Code Online (Sandbox Code Playgroud)

这将生成main.pb.go,它将具有一个名为 的类型Address

var addr Address
err := proto.Unmarshal(bytes, &addr)
Run Code Online (Sandbox Code Playgroud)

如果由于某种原因无法做到这一点(例如,您必须使用描述符动态地进行),则有一些选项。(但它要复杂得多。)

  1. 使用protoc生成的描述符。您可以protoc导出到描述符文件(通过-o标志)。如果这是为了 RPC,让服务器使用服务器反射;然后,您可以使用github.com/jhump/protoreflect/grpcreflect包下载服务器的描述符(大概由 生成protoc)。
  2. 如果您必须以编程方式创建描述符,而不是使用protoc,我建议使用github.com/jhump/protoreflect/desc/builder包来构建它(而不是尝试手动创建原始原型)。例如,您的原始原型是不够的,因为它们没有 parent FileDescriptorProto,它是任何描述符层次结构的根。该构建器包可以为您处理类似的细节(例如,如果需要,它将合成一个父文件描述符)。

无论如何,您想要的描述符是一个desc.Descriptor(来自github.com/jhump/protoreflect/desc包)。上面的提示(使用其他 protoreflect 子包)将返回desc.Descriptor实例。如果您只有原始描述符原型(如在您的示例代码中),您首先要使用desc#CreateFileDescriptor函数将您的*descriptor.FileDescriptorProto变成一个。*desc.FileDescriptor

如果您正在使用protoc它的-o选项来创建描述符集文件(也不要忘记--include_imports标志),您可以加载它并将其转换为*desc.FileDescriptor使用desc#CreateFileDescriptorFromSet函数。下面是一个例子:

bytes, err := ioutil.ReadFile("protoset-from-protoc")
if err != nil {
  panic(err)
}
var fileSet descriptor.FileDescriptorSet
if err := proto.Unmarshal(bytes, &fileSet); err != nil {
  panic(err)
}
fd, err := desc.CreateFileDescriptorFromSet(&fileSet)
if err != nil {
  panic(err)
}
// Now you have a *desc.FileDescriptor in `fd`
Run Code Online (Sandbox Code Playgroud)

一旦你有了那种正确的描述符,你就可以创建一个*dynamic.Message。然后,您可以使用该proto#Unmarshal函数或使用动态消息的Unmarshal方法解组到它。