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

2024-05-14 07:36

本文主要是介绍【实践】给proto的message添加自定义tag,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

背景

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

# proto定义
message Req {string key_name = 1;
}# 生成的.pb.go 中req的定义type Req struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsKeyName            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) nameEnumsAsInts:  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.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsKeyName 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 ~

 

这篇关于【实践】给proto的message添加自定义tag的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/988141

相关文章

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

golang内存对齐的项目实践

《golang内存对齐的项目实践》本文主要介绍了golang内存对齐的项目实践,内存对齐不仅有助于提高内存访问效率,还确保了与硬件接口的兼容性,是Go语言编程中不可忽视的重要优化手段,下面就来介绍一下... 目录一、结构体中的字段顺序与内存对齐二、内存对齐的原理与规则三、调整结构体字段顺序优化内存对齐四、内

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

python实现简易SSL的项目实践

《python实现简易SSL的项目实践》本文主要介绍了python实现简易SSL的项目实践,包括CA.py、server.py和client.py三个模块,文中通过示例代码介绍的非常详细,对大家的学习... 目录运行环境运行前准备程序实现与流程说明运行截图代码CA.pyclient.pyserver.py参

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...

Spring Boot统一异常拦截实践指南(最新推荐)

《SpringBoot统一异常拦截实践指南(最新推荐)》本文介绍了SpringBoot中统一异常处理的重要性及实现方案,包括使用`@ControllerAdvice`和`@ExceptionHand... 目录Spring Boot统一异常拦截实践指南一、为什么需要统一异常处理二、核心实现方案1. 基础组件

SpringBoot项目中Maven剔除无用Jar引用的最佳实践

《SpringBoot项目中Maven剔除无用Jar引用的最佳实践》在SpringBoot项目开发中,Maven是最常用的构建工具之一,通过Maven,我们可以轻松地管理项目所需的依赖,而,... 目录1、引言2、Maven 依赖管理的基础概念2.1 什么是 Maven 依赖2.2 Maven 的依赖传递机

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

在C#中获取端口号与系统信息的高效实践

《在C#中获取端口号与系统信息的高效实践》在现代软件开发中,尤其是系统管理、运维、监控和性能优化等场景中,了解计算机硬件和网络的状态至关重要,C#作为一种广泛应用的编程语言,提供了丰富的API来帮助开... 目录引言1. 获取端口号信息1.1 获取活动的 TCP 和 UDP 连接说明:应用场景:2. 获取硬