【问题标题】:How to cast/convert a Struct to Protobuf?如何将结构转换/转换为 Protobuf?
【发布时间】:2019-07-16 19:49:23
【问题描述】:

我正在做一个个人项目并第一次使用 Go。我正在使用结构来操作数据并将数据存储在文件中,我使用 proto 作为编码器。

在项目中,我的原型定义看起来像这样

message Data {
    string key = 1;
    string value = 2;
}

message Record {
    int64 size = 1;
    Data data = 2;
}

我的结构看起来像这样

type KVData struct {
    Key       string
    Value     string
}

目前,这就是我创建原始数据的方式

kvData := KVData{Key: "name", Value: "A"}

record := &pb.Record{
        Size: 20,
        Data: &pb.Data{Key: "name", Value: "A"},
}

我正在寻找的是一种方法:

record := &pb.Record{
        Size: 20,
        Data: &((pb.Data)kvData), // Won't work
}

// or like Python

record := &pb.Record{
        Size: 20,
        Data: &(pb.Data{**kvData}), // Won't work
}

我尝试使用谷歌搜索,但找不到任何解释如何执行此操作的解决方案。

注意,我不只是想解决这个特定的情况,我还想知道在结构和原型之间进行操作的推荐 Go 方式是什么(仅使用 proto?)?

【问题讨论】:

  • kvData.(pb.Data)
  • 得到这个invalid type assertion: kvData.(pb.Data) (non-interface type KVData on left)
  • 抱歉搞混了,pb.Data(kvData)
  • 现在,cannot convert kvData (type KVData) to type pb.Data 失败了
  • 仅供参考...消息Record 的protobuf 字段编号可能应该是12 - 而不是34。消息Record 独立于消息Data - 因此它们的字段编号是独立的。如果您要取消字段值以实现向后兼容性,则只需从较晚的数字开始。

标签: go struct protocol-buffers proto


【解决方案1】:

你不能,至少在 Go 1.12.7 中不能。

Go 的 Protobuf 编译器为从消息生成的每个结构添加 3 个额外字段:

XXX_NoUnkeyedLiteral         struct{} `json:"-"`
XXX_unrecognized             []byte   `json:"-"`
XXX_sizecache                int32    `json:"-"`

因此,您的 struct 和生成的 struct 具有不同的字段,并且不完全相同,因此不是 assignable

如果两个结构只是标签不同,可以convert it:

type Person struct {
    Name    string
    Address *struct {
        Street string
        City   string
    }
}

var data *struct {
    Name    string `json:"name"`
    Address *struct {
        Street string `json:"street"`
        City   string `json:"city"`
    } `json:"address"`
}

var person = (*Person)(data)  // ignoring tags, the underlying types are identical

您必须手动创建一个新的struct 实例。

【讨论】:

    【解决方案2】:

    你能得到的最接近的是:

    pbData := pb.Data(kvData) // convert kvData struct to pb.Data struct
    
    record := &pb.Record{
            Size: 20,
            Data: &pbData,
    }
    

    注意:不能像这样组合这两个步骤:

    record := &pb.Record{
        Size: 20,
        Data: &(pb.Data(kvData)), // BROKEN: can't get address of a return-value
    }
    

    您可以在这里进行更多实验:https://play.golang.org/p/2AhWi0Khe4l

    编辑: 如果 go 1.8 之前的结构类型不完全相同(相同的标签等),则无法转换它们。更新后的游乐场链接到演示不匹配标签将转换为 go 1.8 或更高版本。

    【讨论】:

    • 我认为 Pawel 对 proto 的回答是正确的。 Proto 添加了一些额外的字段。但是您的回答确实触及了我问题的某些部分,并且很有帮助。谢谢!
    • 啊!该问题不包括您使用的 protoc 版本。是的,使用syntax = "proto3"; 包含额外生成的XXX 字段只是为了进行简单的类型转换会很麻烦。
    【解决方案3】:

    您可以使用第三方库 gogofaster 从您的 .proto 文件生成 go 代码。

    gogofaster 是一种更快的生成工具,它不包含结构中的额外字段 (XXX_*),从而使转换更简单(因为结构现在将是相同的)。

    您可以在这里了解更多信息:https://github.com/gogo/protobuf

    Uber 的 Prototool 也支持 gogofaster。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2018-03-16
      • 2015-11-18
      • 2013-07-01
      • 1970-01-01
      • 2021-08-07
      • 2021-10-18
      • 1970-01-01
      • 2017-02-09
      • 1970-01-01
      相关资源
      最近更新 更多