1.ProtoBuf认识,安装以及用法
参考:[golang 微服务] 3. ProtoBuf认识,安装以及golang 中ProtoBuf使用
2. 使用protoc-go-inject-tag工具注入自定义标签
这里有一个案例:
syntax=proto3; package test;option go_package = ".;test";message MyMessage {int64 Code = 1; }
执行protoc --proto_path=. --go_out=. test.proto导出的test.pb.go里的MyMessage这个结构体的定义会是这样:
type MyMessage struct {state protoimpl.MessageStatesizeCache protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsCode int64 `protobuf:"varint,1,opt,name=Code,proto3" json:"Code,omitempty"` }
可以看到Code字段的protobuf和json的tag都是固定的(目前还没有找到方法能通过protoc命令的参数来设置tag),但是这样的struct有时候并不是我们所期待的,比如下面的代码片段:
msg := &MyMessage{Code: 0} bdata, _ := json.Marshal(msg) fmt.Println(string(bdata))
这段代码最终的输出会是
{}
,因为Code的json tag设置了omitempty,这种情况在开发过程中有时候是很蛋疼的,因为即便Code是默认值0,我们也还是希望能打印出来的。因此我们需要一种方法能通过在编写proto文件的时候,在里面注入tag,然后导出成go的时候这个被注入的字段的tag可以自定义。这时,就可以使用protoc-go-inject-tag工具了, 这个库可以在proto文件中注入tag,然后在导出的时候相应的字段的tag就可以被修改掉了,,步骤如下:
(1).proto-go-inject-tag工具介绍
protoc-go-inject-tag 是一个用于在生成的 Go 结构体中注入自定义标签的工具。在使用 Protocol Buffers(protobuf)生成 Go 代码时,默认情况下生成的.go文件里的结构体标签是没办法灵活设置的。protoc-go-inject-tag 工具允许开发者在生成的 Go 文件中注入自定义的标签,从而提高代码的灵活性和可维护性
(2).下载并安装protoc-go-inject-tag
首先,确保已经安装了
protoc
和protoc-gen-go
,然后安装protoc-go-inject-tag
:go install github.com/favadi/protoc-go-inject-tag@latest
(3)..proto文件创建
这里举个例子: agent.proto文件部分原始代码如下:
syntax = "proto3";
package agent.pb;// 代理数据-统计日报
option go_package = "agent/proto/agentpb";//分组成员(组,成员)详细代理数据model Body
message GroupDetailDataModelBody{repeated GroupDetailDataModel list = 1;
}//获取分组成员(组长,成员)对应的代理相关数据
//分组成员(组,成员)详细代理数据model
message GroupDetailDataModel{uint64 id = 1; // 组id(group_id)/成员id(manager_id)string name = 2; // 名称uint64 new_agent_num = 3; // 新增代理人数uint64 effect_agent_num = 4; // 有效新增代理人数: exp > 0的人数uint64 exp = 5; // 代理充值金额bool is_group = 6; // 当is_group为false时,id表示成员id(也就是manager_id),点击"详情"时,访问的是daily/detailAccount接口; 当is_group为true时,id表示组id(也就是group_id), 点击"详情"时,访问的是daily/detailGroup接口
}
(4).生成.pb.go文件
cd 到proto文件下面,执行下面命令即可:
protoc --go_out=./ agent.proto //一般情况下使用这个命令
protoc --go_out=plugins=grpc:. agent.proto //有RPC服务的情况下使用这个命令
(5).查看.pb.go文件
通过protoc --go_out 生成的agent.pb.go部分结构体如下:
// 获取分组成员(组长,成员)对应的代理相关数据
// 分组成员(组,成员)详细代理数据model
type GroupDetailDataModel struct {state protoimpl.MessageStatesizeCache protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsId uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` // 组id(group_id)/成员id(manager_id)Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // 名称NewAgentNum uint64 `protobuf:"varint,3,opt,name=new_agent_num,json=newAgentNum,proto3" json:"new_agent_num,omitempty"` // 新增代理人数EffectAgentNum uint64 `protobuf:"varint,4,opt,name=effect_agent_num,json=effectAgentNum,proto3" json:"effect_agent_num,omitempty"` // 有效新增代理人数: exp > 0的人数Exp uint64 `protobuf:"varint,5,opt,name=exp,proto3" json:"exp,omitempty"` // 代理充值金额IsGroup bool `protobuf:"varint,6,opt,name=is_group,json=isGroup,proto3" json:"is_group,omitempty"` // 当is_group为false时,id表示成员id(也就是manager_id),点击"详情"时,访问的是daily/detailAccount接口; 当is_group为true时,id表示组id(也就是group_id), 点击"详情"时,访问的是daily/detailGroup接口
}
上述结构体中,json格式中的omitempty表示:如果该属性没有值,则不会显示,也就是说,当对应属性为nil或者没有赋值时,不会返回给前端接口
但是,有时候,却需要返回给前端接口对应的值,不论其值是否存在,这里就需要使用protoc-go-inject-tag工具来自定义标签了,操作如下步骤
(5).使用protoc-go-inject-tag工具注入自定义标签
首先,需要修改proto代码,自定义标签使用语法如下: // @gotags: json:"属性名", 通过设置,然后通过protoc-go-inject-tag -input=./xxx.pb.go 就可以自定义设置属性标签了,修改proto文件如下:
syntax = "proto3";
package agent.pb;// 代理数据-统计日报
option go_package = "agent/proto/agentpb";message GroupDetailDataModelBody{// @gotags: json:"list"repeated GroupDetailDataModel list = 1;
}//获取分组成员(组长,成员)对应的代理相关数据
//分组成员(组,成员)详细代理数据model
message GroupDetailDataModel{// @gotags: json:"id"uint64 id = 1; // 组id(group_id)/成员id(manager_id)// @gotags: json:"name"string name = 2; // 名称// @gotags: json:"new_agent_num"uint64 new_agent_num = 3; // 新增代理人数// @gotags: json:"effect_agent_num"uint64 effect_agent_num = 4; // 有效新增代理人数: exp > 0的人数// @gotags: json:"exp"uint64 exp = 5; // 代理充值金额// @gotags: json:"is_group"bool is_group = 6; // 当is_group为false时,id表示成员id(也就是manager_id),点击"详情"时,访问的是daily/detailAccount接口; 当is_group为true时,id表示组id(也就是group_id), 点击"详情"时,访问的是daily/detailGroup接口
}
上面自定义对应的属性标签,json中只有对应的属性,去掉了 omitempty,也就是说:不论对应的属性是否有值,其属性名称都应该返回,没有值时返回的是默认值
通过protoc-go-inject-tag -input=命令注入自定义标签修该.pb.go文件如下:
protoc-go-inject-tag -input=./agent.pb.go
修改后的.pb.go部分代码如下:
// 分组成员(组,成员)详细代理数据model
type GroupDetailDataModel struct {state protoimpl.MessageStatesizeCache protoimpl.SizeCacheunknownFields protoimpl.UnknownFields// @gotags: json:"id"Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id"` // 组id(group_id)/成员id(manager_id)// @gotags: json:"name"Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name"` // 名称// @gotags: json:"new_agent_num"NewAgentNum uint64 `protobuf:"varint,3,opt,name=new_agent_num,json=newAgentNum,proto3" json:"new_agent_num"` // 新增代理人数// @gotags: json:"effect_agent_num"EffectAgentNum uint64 `protobuf:"varint,4,opt,name=effect_agent_num,json=effectAgentNum,proto3" json:"effect_agent_num"` // 有效新增代理人数: exp > 0的人数// @gotags: json:"exp"Exp uint64 `protobuf:"varint,5,opt,name=exp,proto3" json:"exp"` // 代理充值金额// @gotags: json:"is_group"IsGroup bool `protobuf:"varint,6,opt,name=is_group,json=isGroup,proto3" json:"is_group"` // 当is_group为false时,id表示成员id(也就是manager_id),点击"详情"时,访问的是daily/detailAccount接口; 当is_group为true时,id表示组id(也就是group_id), 点击"详情"时,访问的是daily/detailGroup接口
}
和原始.pb.go比较可看出, json发生了变化, omitempty没有了,这就达到了自定义标签的效果