登录
首页 >  Golang >  Go问答

提取 protobuf 结构中的导出字段的方法

来源:stackoverflow

时间:2024-02-28 16:06:25 449浏览 收藏

在Golang实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天golang学习网就整理分享《提取 protobuf 结构中的导出字段的方法》,聊聊,希望可以帮助到正在努力赚钱的你。

问题内容

我的原型文件如下所示

message deviceoption {
  string apid = 1;
  string other = 2;
}

运行protoc后,生成的deviceoption结构如下:

type deviceoption struct {
    state         protoimpl.messagestate
    sizecache     protoimpl.sizecache
    unknownfields protoimpl.unknownfields

    apid  string `protobuf:"bytes,1,opt,name=apid,proto3" json:"apid,omitempty"`
    other string `protobuf:"bytes,2,opt,name=other,proto3" json:"other,omitempty"`
}

现在,在服务器中我想使用 https://pkg.go.dev/reflect 解析所有可用字段。我的代码是这样的:

v := reflect.ValueOf(pb.DeviceOption{
            ApID: "random1",
            Other: "random2",
        }) 
for i := 0; i < v.NumField(); i++ {
    // Do my thing
}

v.numfield() 返回 5,这意味着它包含所有字段,包括我们没有的 statesizecacheunknownfields不想。我们只需要 apid其他

是否有其他方法可以让 reflect 仅返回数据(导出)字段,而忽略元数据(未导出)字段?


正确答案


方法 numfields 正确返回导出或未导出的字段数。

转到 1.17 及更高版本

使用StructField.IsExported

for _, f := range reflect.visiblefields(v.type()) {
     if f.isexported() {
          // do your thing
     }
}

go 1.16 之前

要了解导出哪些,请检查字段 pkgpath 是否为空。

也可以使用 CanSet,但 go 1.17 中 structfield.isexported 的实现是 literally f.pkgpath == ""

有关 pkgpath 字段的文档指出:

pkgpath 是限定小写(未导出)字段名称的包路径。对于大写(导出)字段名称,它为空。查看https://golang.org/ref/spec#Uniqueness_of_identifiers

typ := v.type()
for i := 0; i < typ.numfield(); i++ {
    if f := typ.field(i); f.pkgpath == "" {
         // do your thing
    }
}

在 protobuf 编译器生成的结构类型上使用 reflect 很可能不是一个好主意。它们的确切结构应被视为实现细节。某些版本的编译器用于生成名为 xxx_nounkeyedliteralxxx_unrecognizedxxx_sizecache 的导出字段,如果仅考虑 go 类型的结构,这些字段将被视为“数据”字段。我认为没有任何具体保证不会再次发生。

google.golang.org/protobuf/reflect/protoreflect 包提供了动态检查消息的方法。给定一条消息 m,您可以执行以下操作来覆盖其上设置的所有字段:

proto.MessageReflect(m).Range(
  func (field protoreflect.MessageDescriptor, value protoreflect.Value) bool {
    // return `true` to continue, `false` to break
    ...
  })

或者您可以使用 proto.messagereflect(m).descriptor().fields() 来获取表示消息中声明的字段列表的接口。

另请参阅:A new Go API for Protocol Buffers

以上就是《提取 protobuf 结构中的导出字段的方法》的详细内容,更多关于的资料请关注golang学习网公众号!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>