自定義protoc-gen-go生成Go結構體,統一字段命名與JSON標簽風格

背景

在日常的 Go 微服務開發中,Protocol Buffers(protobuf) 是廣泛使用的數據交換格式。其配套工具 protoc-gen-go 會根據 .proto 文件生成 Go 結構體代碼,但默認生成的字段名、JSON tag 命名風格往往不能滿足所有團隊或項目的代碼規范需求。

比如,團隊可能有以下規范或訴求:

  • Go 結構體字段名需要使用特定的 PascalCase 命名規則;
  • JSON tag 必須統一為 snake_case,以與前端規范對齊;
  • 字段名稱如 iduser_id等在 Go 代碼中必須轉換為IDUserID,以保持一致性和清晰性。

遺憾的是,protoc-gen-go 并沒有提供原生的機制來滿足這些細致的定制需求。因此,我們選擇自定義 protoc-gen-go 插件的生成邏輯,以實現結構體字段命名的精細控制。

實現步驟

1. 獲取protobuf-go源碼

首先克隆指定版本的protobuf-go倉庫:

git clone github.com/protocolbuffers/protobuf-go@v1.36.6

2. 定義全局參數

cmd/protoc-gen-go/internal_gengo/main.go文件中定義全局變量和常量:

var (IsCustomField bool   // 標識是否使用自定義字段命名TagJSONStyle  string // JSON標簽命名風格
)const (SnakeCaseStyle = "snake_case"CamelCaseStyle = "camel_case"
)

3. 聲明命令行參數

cmd/protoc-gen-go/main.go中添加參數解析邏輯:

// 定義option參數
isCustomField = flags.Bool("is_custom_field", false, "struct field naming style setting, default is false, indicates no modification, "+"if true indicates custom hump style, for example, message field name suffix "+"is _id or Id, struct field name suffix of generated go code is ID")tagJSONStyle = flags.String("tag_json_style", "", "struct field tag json naming style setting, default is empty, indicates no modification, "+"if set to 'snake_case', indicates snake case style, if set to 'camelCase', indicates camel case style. "+"NOTE: this option overrides protobuf's json_name option")// 判斷參數是否合法
if *isCustomField {gengo.IsCustomField = true
}
if *tagJSONStyle != "" {if *tagJSONStyle != gengo.SnakeCaseStyle && *tagJSONStyle != gengo.CamelCaseStyle {return fmt.Errorf("protoc-gen-go: invalid tag_json_style value: %q, "+"must be 'camel_case' or'snake_case'", *tagJSONStyle)}gengo.TagJSONStyle = *tagJSONStyle
}

4. 添加命名轉換工具

從nameFormat.go復制代碼到cmd/protoc-gen-go/internal_gengo目錄,并將函數名xstrings.ToCamelCase修改為xstrings.ToPascalCase

5. 修改字段生成邏輯

generateOneFile函數中添加自定義字段命名邏輯:

func generateOneFile(gen *protogen.Plugin, file *protogen.File, f *fileInfo, variant string) *protogen.GeneratedFile {// ......for _, message := range f.allMessages {// 添加的自定義字段名風格設置if IsCustomField {for _, field := range message.Fields {field.GoName = toCamel(field.GoName)}}genMessage(g, f, message)}// ......
}

6. 修改JSON標簽生成邏輯

更新fieldJSONTagValue函數以支持自定義JSON標簽風格:

func fieldJSONTagValue(field *protogen.Field) string {switch TagJSONStyle {case SnakeCaseStyle:return customToSnake(string(field.Desc.Name())) + ",omitempty"case CamelCaseStyle:return customToCamel(string(field.Desc.Name())) + ",omitempty"}return string(field.Desc.Name()) + ",omitempty" // default
}

使用示例

測試proto文件

使用以下user.proto文件進行測試:

syntax = "proto3";package api.user.v1;option go_package = "user/api/user/v1;v1";service user {// Login 登錄rpc Login(LoginRequest) returns (LoginReply) {}
}message LoginRequest {string email = 1;string password = 2;
}message LoginReply {uint64 user_id =1;uint64 communityId=2;repeated uint64 roleIDs =3;string token =4;
}

生成命令對比

  1. 默認生成方式(與原始protobuf-go行為一致):
protoc --go_out=. --go_opt=paths=source_relative user.proto
  1. 自定義字段命名
protoc --go_out=. --go_opt=paths=source_relative --go_opt=is_custom_field=true user.proto
  1. 自定義字段命名+蛇形JSON標簽
protoc --go_out=. --go_opt=paths=source_relative --go_opt=is_custom_field=true --go_opt=tag_json_style=snake_case user.proto

效果對比

默認生成的代碼

type LoginReply struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsUserId      uint64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`CommunityId uint64 `protobuf:"varint,2,opt,name=communityId,json=communityId,proto3" json:"communityId,omitempty"`RoleIDs     []uint64 `protobuf:"varint,3,rep,packed,name=roleIDs,json=roleIDs,proto3" json:"roleIDs,omitempty"`Token       string `protobuf:"bytes,4,opt,name=token,proto3" json:"token,omitempty"`
}

自定義字段命名后的代碼

type LoginReply struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsUserID      uint64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`CommunityID uint64 `protobuf:"varint,2,opt,name=communityId,json=communityId,proto3" json:"communityId,omitempty"`RoleIDs     []uint64 `protobuf:"varint,3,rep,packed,name=roleIDs,json=roleIDs,proto3" json:"roleIDs,omitempty"`Token       string `protobuf:"bytes,4,opt,name=token,proto3" json:"token,omitempty"`
}

自定義字段命名+蛇形JSON標簽后的代碼

type LoginReply struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsUserID      uint64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`CommunityID uint64 `protobuf:"varint,2,opt,name=communityId,json=communityId,proto3" json:"community_id,omitempty"`RoleIDs     []uint64 `protobuf:"varint,3,rep,packed,name=roleIDs,json=roleIDs,proto3" json:"role_ids,omitempty"`Token       string `protobuf:"bytes,4,opt,name=token,proto3" json:"token,omitempty"`
}

總結

通過修改protoc-gen-go源碼,我們實現了以下功能:

  1. 統一結構體字段命名風格(如將ID后綴統一大寫)
  2. 控制JSON標簽的命名風格(蛇形或駝峰)
  3. 通過命令行參數靈活控制生成行為

這種定制化特別適合需要嚴格遵循特定代碼規范的團隊,可以確保生成的代碼風格一致,減少人工修改的工作量。

注意事項

  1. 此修改基于protobuf-go v1.36.6版本,其他版本可能需要相應調整
  2. 自定義JSON標簽風格會覆蓋protobuf原生的json_name選項
  3. 建議團隊內部統一使用規范,避免風格不一致

希望本文對需要自定義protobuf代碼生成的開發者有所幫助!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/84081.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/84081.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/84081.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

LabVIEW的MathScript Node 繪圖功能

該VI 借助 LabVIEW 的 MathScript Node,結合事件監聽機制,實現基于 MathScript 的繪圖功能,并支持通過交互控件自定義繪圖屬性。利用 MathScript 編寫腳本完成圖形初始化,再通過LabVIEW 事件結構響應用戶操作,動態修改…

GD圖像處理與SESSiON

SESSION: 原理: session與瀏覽器無關,但是與cookie有關 1.PHP碰到session_start()時開啟session會話,會自動檢測sessionID a. 如果cookie中存在,使用現成的 b. 如果cookie中不存在,創建一個sessionID,并通過響應頭以cookie形式保存到瀏覽…

【Web應用】若依框架:基礎篇14 源碼閱讀-后端代碼分析-課程管理模塊前后端代碼分析

文章目錄 一、課程管理模塊前端代碼截圖二、前端代碼及分析index.vuecourse.js 三、前端執行流程1. 組件初始化2. 查詢操作3. 列表操作4. 對話框操作5. API 請求6. 執行流程總結關鍵點 四、課程管理模塊后端代碼截圖五、后端代碼塊CourseControllerICourseServiceCourseMapperC…

深入理解系統:UML類圖

UML類圖 類圖(class diagram) 描述系統中的對象類型,以及存在于它們之間的各種靜態關系。 正向工程(forward engineering)在編寫代碼之前畫UML圖。 逆向工程(reverse engineering)從已有代碼建…

DeepSeek12-Open WebUI 知識庫配置詳細步驟

📚 Open WebUI 知識庫配置詳細步驟(中英文對照) 🌐 界面語言切換 # 首次登錄后切換語言: 1. 點擊左下角用戶頭像 → Settings 2. 在 "General" 選項卡找到 "Language" 3. 選擇 中文(簡體)/Engli…

Python網絡設備批量配置腳本解析

目錄 腳本概述 代碼解析 導入模塊 日志配置 核心函數config_device 主程序邏輯 使用說明 腳本優化建議 完整代碼 腳本概述 這是一個使用Python編寫的網絡設備批量配置腳本,主要功能是通過SSH協議批量登錄多臺網絡設備(如路由器、交換機等&…

Z-FOLD: A Frustratingly Easy Post-Training Quantization Scheme for LLMs

文章目錄 摘要1 引言2 相關工作2.1 量化2.2 大型語言模型的量化 3 Z-FOLD3.1 新引入的參數 ζ3.2 參數整合(ζ 折疊)3.3 使用校準集的微調 4 實驗4.1 實驗設置4.2 與其他方法的比較4.3 Z-FOLD 的泛化能力4.4 Z-FOLD 的可移植性4.5 消融研究 5 結論6 限制…

交流電機深度解析:從基礎到實戰的全面指南

簡介 交流電機作為現代工業中不可或缺的動力設備,廣泛應用于各個領域。本文將帶你深入了解交流電機,從最基礎的概念和工作原理開始,逐步介紹其類型、結構、關鍵參數等基礎知識。同時,我們會探討交流電機在企業級開發研發中的技術實戰,包括控制技術、調速方法、建模與仿真…

【靶場】XXE-Lab xxe漏洞

前言 學習xxe漏洞,搭了個XXE-Lab的靶場 一、搭建靶場 現在需要登錄,不知道用戶名密碼,先隨便試試抓包 二、判斷是否存在xxe漏洞 1.首先登錄抓包 看到xml數據解析,由此判斷和xxe漏洞有關,但還不確定xxe漏洞是否存在。 2.嘗試xxe 漏洞 判斷是否存在xxe漏洞 A.send to …

【C++特殊工具與技術】優化內存分配(三):operator new函數和opertor delete函數

目錄 一、基礎概念:operator new與operator delete的本質 1.1 標準庫提供的operator new接口 1.2 標準庫operator delete的接口 1.3 關鍵特性總結 二、new表達式與operator new的調用鏈解析 2.1 new表達式的底層步驟 2.2 示例:觀察new表達式的調用…

[c#]判定當前軟件是否用管理員權限打開

有時一些軟件的邏輯中需要使用管理員權限對某些文件進行修改時,那么該軟件在執行或者打開的場合,就需要用使用管理員身份運行才能達到效果。那么在c#里,如何判定該軟件是否是對管理員身份運的呢? 1.取得當前的windows用戶。 2.取得…

如果在main中拋出異常,該如何處理

#采用 setDefaultUncaughtExceptionHandler 進行全局兜底 public static void main(String[] args) { Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> { System.err.println("全局捕獲異常: " ex.getMessage()); ex.printStackTrace(); System.exi…

HBM 讀的那些事

如下所示,為HBM讀的時序。注意這里說的HBM是和HBM3是有區別的. RL 的配置,是通過MR2來實現的 WDQS貌似和CK同頻。這幅圖告訴你,WDQS和CK的源頭是一樣的,都來自PLL,而且中間沒有經過倍頻操作。所以兩者頻率基本是一致的。這是HBM的…

省略號和可變參數模板

本文主要介紹如何展開可變參數的參數包 1.C語言的va_list展開可變參數 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 聲明va_list類型的變量va_list args;// 使用va_start將可變參數寫入變量argsva_start(args, count);for (in…

三十五、面向對象底層邏輯-Spring MVC中AbstractXlsxStreamingView的設計

在Web應用開發中&#xff0c;大數據量的Excel導出功能是常見需求。傳統Apache POI的XSSF實現方式在處理超大數據集時&#xff0c;會因全量加載到內存導致OOM&#xff08;內存溢出&#xff09;問題。Spring MVC提供的AbstractXlsxStreamingView通過流式處理機制&#xff0c;有效…

【大模型:知識圖譜】--3.py2neo連接圖數據庫neo4j

【圖數據庫】--Neo4j 安裝_neo4j安裝-CSDN博客 需要打開圖數據庫Neo4j&#xff0c; neo4j console 目錄 1.圖數據庫--連接 2.圖數據庫--操作 2.1.創建節點 2.2.刪除節點 2.3.增改屬性 2.4.建立關系 2.5.查詢節點 2.6.查詢關系 3.圖數據庫--實例 1.圖數據庫--連接 fr…

基于dify的營養分析工作流:3分鐘生成個人營養分析報告

你去醫院做體檢&#xff0c;需要多久拿到體檢報告呢&#xff1f;醫院會為每位病人做一份多維度的健康報告嗎&#xff1f;"人工報告需1小時/份&#xff1f;數據誤差率高達35%&#xff1f;傳統工具無法個性化&#xff1f; Dify工作流AI模型的組合拳&#xff0c;正在重塑健康…

Web后端基礎(基礎知識)

BS架構&#xff1a;Browser/Server&#xff0c;瀏覽器/服務器架構模式。客戶端只需要瀏覽器&#xff0c;應用程序的邏輯和數據都存儲在服務端。 優點&#xff1a;維護方便缺點&#xff1a;體驗一般 CS架構&#xff1a;Client/Server&#xff0c;客戶端/服務器架構模式。需要單獨…

MySQL(56)什么是復合索引?

復合索引&#xff08;Composite Index&#xff09;&#xff0c;也稱為多列索引&#xff0c;是在數據庫表的多列上創建的索引。它可以提高涉及多個列的查詢性能&#xff0c;通過組合多個列的值來索引數據。復合索引特別適用于需要同時過濾多列的查詢。 復合索引的優點 提高多列…

高并發下的緩存擊穿/雪崩解決方案

有效解決緩存擊穿和雪崩的方法包括&#xff1a;1. 使用互斥鎖處理緩存擊穿&#xff1b;2. 采用熔斷器模式防止雪崩&#xff1b;3. 實施緩存預熱和降級策略&#xff1b;4. 利用分片和多級緩存分散請求壓力。這些方法各有優劣&#xff0c;需根據實際業務場景靈活調整和結合使用。…