【实践】给proto的message添加自定义tag

背景

通常来说, 使用proto定义message的Field是使用下划线,比如:

# proto定义
message Req {

  string key_name = 1;
}


# 生成的.pb.go 中req的定义

type Req struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	KeyName            string            `protobuf:"bytes,1,opt,name=key_name,json=keyName,proto3" json:"key_name,omitempty"`

注意: proto的定义,现在默认推荐proto3, 所以此处忽略 proto2中可以给字段声明optional和required。

不过,需要特别注意一下tag中如果添加了omitempty选项,proto2和proto3中对于默认值的处理有所不同。

比如:proto2中对于添加了optional的bool类型,生成的字段是个指针类型,默认值为nil才是empty,false就不是默认值了,而对于proto3中bool的定义,默认值就是false,对于添加omitempty的tag来说就是空了(切记)

从生产的pb.go文件可以看到, 默认生成的json tag是根据字段名的定义来的

可能你会疑惑: protobuf声明的tag中有name=key_name和json=keyName的两者,这应该是用在不同marshal和unMarshal的场景, 比如之前提到的jsonpb库,可以指定marshal的选项: OrigName= true / fasle: 

import (
  "github.com/gogo/protobuf/jsonpb"
  "encoding/json"
)

marshaler := &jsonpb.Marshaler{
		OrigName:     false,  // 是否按字段的声明(.proto) name
		EnumsAsInts:  true,
		EmitDefaults: true,
	}


# 一个req 示例
req := &Req {
  KeyName: "123",
}


# OrigName=false
out, _ := marshaler.MarshalToString(req) 
t.Logf("out of jsopb:%v\n", out). // {"keyName":"123"}

outJson, _ := json.Marshal(req)
t.Logf("out of json:%v\n", string(outJson)) // {"key_name":"123"}

# OrigName=true
marshaler = jsonpb.Marshaler{
		OrigName: true,
	}

out, _ = marshaler.MarshalToString(req)
t.Logf("out of jsopb:%v\n", out). // {"key_name":"123"}
  • OrigName= true:  此时就是按照protobuf声明的tag中name=key_name来处理的
  • OrigName= false:   此时就是按照protobuf声明的tag中json=keyName来处理的

 

到此阶段, pb能生成的基本就是这样的。那如果我想添加自定义的tag呢? 

比如: keyName 映射成 key_real_name 等, 这样的需求应该还不少

添加自定义tag

可以使用这个工具来给Field添加

先安装一下:

go install github.com/favadi/protoc-go-inject-tag@latest

修改对应的proto定义, 然后执行:protoc-go-inject-tag -input="./*.pb.go"

message Req {
	string key_name = 1; // @gotags: json:"key_real_name" bson:"key_in_monogo" orm:"key_in_orm"
}


# 对应的pb生产文件:
type Req struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	KeyName string `protobuf:"bytes,1,opt,name=key_name,json=keyName,proto3" json:"key_real_name" bson:"key_in_monogo" orm:"key_in_orm"` // @gotags: json:"key_real_name" bson:"key_in_monogo" orm:"key_in_orm"
}

可以看到,通过@gotags 自定义的tag声明, 生效了。

1. json的tag可以指定新的名称

2. 添加了用于映射mongodb中字段的bson tag

3. 同时, 还添加了orm组件的字段tag 映射名称

是不是很方便, Enjoy ~

小技巧

问题: 如果想要复用proto的message定义, 一般可以直接在proto中引用对应message的定义即可, 那如果我不行新增一个层级呢?

正常使用情况:

message Req {
	string key_name = 1; // @gotags: json:"key_real_name" bson:"key_in_monogo" orm:"key_in_orm"
}

message ReqMeta {
	string meta = 1;
}

// 引用Req和ReqMeta
message ReqAndMeta {
	Req req = 1;
	ReqMeta meta = 2;
}

 如果是这样, json格式为:

{"req":{"key_real_name":"123"},"meta":{"meta":"meta123"}}

 

如果这就是你想要的, 那通过在proto中定义一下就可以了。

如果你不想要新增一个req和meta层级呢?

直接在proto中引用定义肯定不行, 但是可以使用golang的特性: 组合

type ReqAndMetaCombine struct {
	*Req
	*ReqMeta
}

通过组合多个proto中的message,最后的json格式如下:

{"key_real_name":"123","meta":"meta123"}

确实少了一个层级, it works ~

 

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JYCJ_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值