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

时间:2019-07-17 21:32:37

标签: go protocol-buffers

我有一个文件,其中包含以下protomessage的一部分字节。

syntax = "proto3";

package main;

message Address {
    string street = 1;
    string country = 2;
    string state = 3;
}

我的消息类型描述如下:

func GetProtoDescriptor() (*descriptor.DescriptorProto, error) {

    return &descriptor.DescriptorProto{
        Name: proto.String("Address"),
        Field: []*descriptor.FieldDescriptorProto{
            &descriptor.FieldDescriptorProto{
                Name:     proto.String("street"),
                JsonName: proto.String("street"),
                Number:   proto.Int(1),
                Label:    descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
                Type:     descriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
            },

            &descriptor.FieldDescriptorProto{
                Name:     proto.String("state"),
                JsonName: proto.String("state"),
                Number:   proto.Int(2),
                Label:    descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
                Type:     descriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
            },

            &descriptor.FieldDescriptorProto{
                Name:     proto.String("country"),
                JsonName: proto.String("country"),
                Number:   proto.Int(2),
                Label:    descriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
                Type:     descriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
            },
        },
    }, nil

}

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

谢谢您的帮助。

1 个答案:

答案 0 :(得分:2)

典型的方法是使用protoc 编译协议以编写Go代码:

protoc main.proto --go_out=.

这将生成main.pb.go,其类型为Address

var addr Address
err := proto.Unmarshal(bytes, &addr)

如果由于某种原因这不可行(例如,您必须使用描述符动态地执行此操作),则有一些选择。 (但这要复杂得多。)

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

无论如何,所需的描述符是desc.Descriptor(来自github.com/jhump/protoreflect/desc包)。上面的技巧(使用其他protoreflect子程序包)将返回*descriptor.FileDescriptorProto个实例。如果只有原始描述符原型(如示例代码中所示),则希望首先使用desc#CreateFileDescriptor函数将*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` 。这是一个示例:

proto#Unmarshal

一旦有了正确的描述符,就可以创建*dynamic.Message。然后,您可以使用Unmarshal函数或动态消息的Sub ffs() Dim wbkS As Workbook Dim wshS As Worksheet Dim wshT As Worksheet Dim strFile As String With Application.FileDialog(1) ' msoFileDialogOpen .Filters.Clear .Filters.Add "Excel workbooks", "*.xls;*.xlsx;*.xlsb;*.xlsm" .InitialFileName = "D:\Macro testing area" If .Show Then strFile = .SelectedItems(1) Else MsgBox "No file selected", vbCritical Exit Sub End If End With Application.ScreenUpdating = False Set wshT = ActiveSheet Set wbkS = Workbooks.Open(Filename:=strFile, ReadOnly:=True) Set wshS = wbkS.Worksheets("Data") Set wshS1 = wbkS.Worksheets("Section 1") wshT.Range("A1:L88").Value = wshS.Range("A1:L88").Value ' Optional: close source workbook wbkS.Close SaveChanges:=False Application.ScreenUpdating = True End Sub 方法将其编组。