无需预编译 Go 代码即可将 protobuf 序列化消息转换为 JSON

ken*_*tor 3 go protocol-buffers

我想将 protobuf 序列化消息转换为人类可读的 JSON 格式。我面临的主要问题是我需要在不事先将 proto 描述符编译为 Go 代码的情况下执行此操作。我可以.proto在运行时访问文件,但不能在编译时访问。

我的印象是新的 Protobuf API v2 ( https://github.com/protocolbuffers/protobuf-go ) 支持动态反序列化(请参阅包types/dynamicpb),但我显然无法弄清楚如何使用它:

func readDynamically(in []byte) {
    // How do I load the required descriptor (for NewMessage()) from my `addressbook.proto` file?)
    descriptor := ??

    msg := dynamicpb.NewMessage(descriptor)
    err := protojson.Unmarshal(in, msg)
    if err != nil {
        panic(err)
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码注释了我的问题:如何dynamicpb.NewMessage().proto文件中获取所需的描述符?

Rik*_*iki 5

应该像这样使用dynamicpb包。

func readDynamically(in []byte) {
    registry, err := createProtoRegistry(".", "addressbook.proto")
    if err != nil {
        panic(err)
    }

    desc, err := registry.FindFileByPath("addressbook.proto")
    if err != nil {
        panic(err)
    }
    fd := desc.Messages()
    addressBook := fd.ByName("AddressBook")

    msg := dynamicpb.NewMessage(addressBook)
    err = proto.Unmarshal(in, msg)
    jsonBytes, err := protojson.Marshal(msg)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(jsonBytes))
    if err != nil {
        panic(err)
    }
}

func createProtoRegistry(srcDir string, filename string) (*protoregistry.Files, error) {
    // Create descriptors using the protoc binary.
    // Imported dependencies are included so that the descriptors are self-contained.
    tmpFile := filename + "-tmp.pb"
    cmd := exec.Command("./protoc/protoc",
        "--include_imports",
        "--descriptor_set_out=" + tmpFile,
        "-I"+srcDir,
        path.Join(srcDir, filename))

    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    err := cmd.Run()
    if err != nil {
        return nil, err
    }
    defer os.Remove(tmpFile)

    marshalledDescriptorSet, err := ioutil.ReadFile(tmpFile)
    if err != nil {
        return nil, err
    }
    descriptorSet := descriptorpb.FileDescriptorSet{}
    err = proto.Unmarshal(marshalledDescriptorSet, &descriptorSet)
    if err != nil {
        return nil, err
    }

    files, err := protodesc.NewFiles(&descriptorSet)
    if err != nil {
        return nil, err
    }

    return files, nil
}
Run Code Online (Sandbox Code Playgroud)