这个教程中将会描述protocol buffer编译器通过给定的.proto会编译生成什么Go代码。教程针对的是proto3版本的protobuf。在阅读之前确保你已经阅读过Protobuf语言指南。

编译器调用

Protobuf核心的工具集是C++语言开发的,官方的protoc编译器中并不支持Go语言,需要安装一个插件才能生成Go代码。用如下命令安装:

$ go get github.com/golang/protobuf/protoc-gen-go

提供了一个protoc-gen-go二进制文件,当编译器调用时传递了--go_out命令行标志时protoc就会使用它。--go_out告诉编译器把Go源代码写到哪里。编译器会为每个.proto文件生成一个单独的源代码文件。

输出文件的名称是通过获取.proto文件的名称并进行两处更改来计算的:

  • 生成文件的扩展名是.pb.go。比如说player_record.proto编译后会得到player_record.pb.go
  • proto路径(使用--proto_path-I命令行标志指定)将替换为输出路径(使用--go_out标志指定)。

当你运行如下编译命令时:

protoc --proto_path=src --go_out=build/gen src/foo.proto src/bar/baz.proto

编译器会读取文件src/foo.protosrc/bar/baz.proto,这将会生成两个输出文件build/gen/foo.pb.gobuild/gen/bar/baz.pb.go

如果有必要,编译器会自动生成build/gen/bar目录,但是他不能创建build或者build/gen目录,这两个必须是已经存在的目录。

如果一个.proto文件中有包声明,生成的源代码将会使用它来作为Go的包名,如果.proto的包名中有. 在Go包名中会将.转换为_。举例来说proto包名example.high_score将会生成Go包名example_high_score

.proto文件中可以使用option go_package指令来覆盖上面默认生成Go包名的规则。比如说包含如下指令的一个.proto文件

package example.high_score;
option go_package = "hs";

生成的Go源代码的包名是hs

如果一个.proto文件中不包含package声明,生成的源代码将会使用.proto文件的文件名(去掉扩展名)作为Go包名,.会被首先转换为_。举例来说一个名为high.score.proto不包含pack声明的文件将会生成文件high.score.pb.go,他的Go包名是high_score

消息

一个简单的消息声明:

message Foo {}

protocol buffer编译器将会生成一个名为Foo的结构体,实现了proto.Message接口的Foo类型的指针

type Foo struct {
}// 重置proto为默认值
func (m *Foo) Reset()         { *m = Foo{} }// String 返回proto的字符串表示
func (m *Foo) String() string { return proto.CompactTextString(m) }// ProtoMessage作为一个tag 确保其他人不会意外的实现
// proto.Message 接口.
func (*Foo) ProtoMessage()    {}

内嵌的消息

一个message可以声明在其他message的内部。比如说:

message Foo {message Bar {}
}

这种情况,编译器会生成两个结构体:FooFoo_Bar

预定义消息类型

Protobufs带有一组预定义的消息,称为众所周知的类型(WKT)。这些类型可以用于与其他服务的互操作性,或者仅仅因为它们简洁地表示了常见的有用模式。例如,Struct消息表示任意C样式结构的格式。

WKT的预生成Go代码作为Go protobuf库的一部分进行分发,如果message中使用了WKT,则生成的消息的Go代码会引用此代码。例如,给出如下消息:

import "google/protobuf/struct.proto"
import "google/protobuf/timestamp.proto"message NamedStruct {string name = 1;google.protobuf.Struct definition = 2;google.protobuf.Timestamp last_modified = 3;
}

生成的Go代码将会像下面这样:

import google_protobuf "github.com/golang/protobuf/ptypes/struct"
import google_protobuf1 "github.com/golang/protobuf/ptypes/timestamp"...type NamedStruct struct {Name         stringDefinition   *google_protobuf.StructLastModified *google_protobuf1.Timestamp
}

一般来说,您不需要将这些类型直接导入代码中。但是,如果需要直接引用其中一种类型,只需导入github.com/golang/protobuf/ptypes/[TYPE]包,并正常使用该类型。

字段

编译器会为每个在message中定义的字段生成一个Go结构体的字段,字段的确切性质取决于它的类型以及它是singularrepeatedmap还是oneof字段。

注意生成的Go结构体的字段将始终使用驼峰命名,即使在.proto文件中消息字段用的是小写加下划线(应该这样)。大小写转换的原理如下:

  • 首字母会大些,如果message中字段的第一个字符是_,它将被替换为X。
  • 如果内部下划线后跟小写字母,则删除下划线,并将后面跟随的字母大写。

因此,proto字段foo_bar_baz在Go中变成FooBarBaz_my_field_name_2变为XMyFieldName_2

单一标量字段

对于字段定义:

int32 foo = 1;

编译器将生成一个带有名为Foo的int32字段和一个访问器方法GetFoo()的结构,该方法返回Foo中的int32值或该字段的零值(如果字段未设置(数值型零值为0,字符串为空字符串))。

单一message字段

给出如下消息类型

message Bar {}

对于一个有Bar类型字段的消息:

// proto3
message Baz {Bar foo = 1;
}

编译器将会生成一个Go结构体

type Baz struct {Foo *Bar
}

消息类型的字段可以设置为nil,这意味着该字段未设置,有效清除该字段。这不等同于将值设置为消息结构体的“空”实例。

编译器还生成一个func(m * Baz)GetFoo()* Bar辅助函数。这让不在中间检查nil值进行链式调用成为可能。

可重复字段

每个重复的字段在Go中的结构中生成一个T类型的slice,其中T是字段的元素类型。对于带有重复字段的此消息:

message Baz {repeated Bar foo = 1;
}

编译器会生成如下结构体:

type Baz struct {Foo  []*Bar
}

同样,对于字段定义repeated bytes foo = 1;编译器将会生成一个带有类型为[][]byte名为Foo的字段的Go结构体。对于可重复的枚举repeated MyEnum bar = 2;,编译器会生成带有类型为[]MyEnum名为Bar的字段的Go结构体。

映射字段

每个映射字段会在Go的结构体中生成一个map[TKey]TValue类型的字段,其中TKey是字段的键类型TValue是字段的值类型。对于下面这个消息定义:

message Bar {}message Baz {map<string, Bar> foo = 1;
}

编译器生成Go结构体

type Baz struct {Foo map[string]*Bar
}

枚举

给出如下枚举

message SearchRequest {enum Corpus {UNIVERSAL = 0;WEB = 1;IMAGES = 2;LOCAL = 3;NEWS = 4;PRODUCTS = 5;VIDEO = 6;}Corpus corpus = 1;...
}

编译器将会生成一个枚举类型和一系列该类型的常量。

对于消息中的枚举(像上面那样),类型名字以消息名开头

type SearchRequest_Corpus int32

对于包级别的枚举:

// .proto
enum Foo {DEFAULT_BAR = 0;BAR_BELLS = 1;BAR_B_CUE = 2;
}

Go 中的类型不会对proto中的枚举名称进行修改:

type Foo int32

此类型具有String()方法,该方法返回给定值的名称。

Enum()方法使用给定值初始化新分配的内存并返回相应的指针:

func (Foo) Enum() *Foo

编译器为枚举中的每个值生成一个常量。对于消息中的枚举,常量以消息的名称开头:

const (SearchRequest_UNIVERSAL SearchRequest_Corpus = 0SearchRequest_WEB       SearchRequest_Corpus = 1SearchRequest_IMAGES    SearchRequest_Corpus = 2SearchRequest_LOCAL     SearchRequest_Corpus = 3SearchRequest_NEWS      SearchRequest_Corpus = 4SearchRequest_PRODUCTS  SearchRequest_Corpus = 5SearchRequest_VIDEO     SearchRequest_Corpus = 6
)

对于包级别的枚举,常量以枚举名称开头:

const (Foo_DEFAULT_BAR Foo = 0Foo_BAR_BELLS   Foo = 1Foo_BAR_B_CUE   Foo = 2
)

protobuf编译器还生成从整数值到字符串名称的映射以及从名称到值的映射:

var Foo_name = map[int32]string{0: "DEFAULT_BAR",1: "BAR_BELLS",2: "BAR_B_CUE",
}
var Foo_value = map[string]int32{"DEFAULT_BAR": 0,"BAR_BELLS":   1,"BAR_B_CUE":   2,
}

请注意,.proto语言允许多个枚举符号具有相同的数值。具有相同数值的符号是同义词。这些在Go中以完全相同的方式表示,多个名称对应于相同的数值。反向映射包含数字值的单个条目,数值映射到出现在proto文件中首先出现的名称。

服务

默认情况下,Go代码生成器不会为服务生成输出。如果您启用gRPC插件(请参阅gRPC Go快速入门指南),则会生成代码以支持gRPC。

Protobuf生成Go代码指南相关推荐

  1. Protobuf生成Java代码(命令行)

    1.说明 本文介绍Protobuf生成Java代码的方法, 下载必须的Protobuf工具, 然后通过命令行, 把.proto文件生成Java代码. 2.准备Protobuf工具 2.1.获取prot ...

  2. Protobuf生成Java代码(Maven)

    1.说明 本文介绍Protobuf生成Java代码的方法, 配置对应的Maven插件, 把.proto文件生成Java代码. 2.插件配置 创建Maven工程grpc-compile, 修改pom.x ...

  3. ProtoBuf 生成 Go 代码去掉 JSON tag omitempty

    文章目录 1.背景 2.定义 proto 文件 3.安装 protoc 和 protoc-gen-go 4. 编译 proto 文件 5.自定义选项(Custom Options) 5.1 简介 5. ...

  4. M1 芯片maven 编译protobuf生成Java代码时,不能找到protoc-gen-grpc-java:exe:osx-aarch_64问题记录

    背景描述: 换电脑后使在m1芯片编译protobuf的maven插件,来生成项目代码,发现无法下载到arm版本的插件,报错如图: 本地使用 brew install protobuf安装的最新版本: ...

  5. 通信协议以及protobuf使用、语法指南一

    文章目录 1.设计通信协议 2.协议设计范例 范例一 IM即时通讯协议 范例二 云平台节点服务器 3.序列化和反序列化基础 4.xml/json/protobuf对比及使用场景 5.protobuf安 ...

  6. 使用JAnnocessor生成Java代码

    在本文中,我将向你展示如何生成的代码JAnnocessor通过创建框架Nikolche Mihajlovski . 在Nikolche的演讲中,我第一次在GeeCON 2012大会上遇到JAnnoce ...

  7. Unity C# 网络学习(十二)——Protobuf生成协议

    Unity C# 网络学习(十二)--Protobuf生成协议 一.安装 去Protobuf官网下载对应操作系统的protoc,用于将.proto文件生成对应语言的协议语言文件 由于我使用的是C#所以 ...

  8. Windows系统kratos生成proto代码是报exec: “protoc“: executable file not found in %PATH%错误解决办法

    前言 今天用kratos的cli工具生成生成 Proto 代码时直接报错了,直接报: exec: "protoc": executable file not found in %P ...

  9. egret protobuf生成ts文件报错问题

    使用egret protobuf生成ts文件报错问题 首先看白鹭官网提供的demo egret protobuf 按步骤安装完成后 添加一个test.proto文件,内容如下 package test ...

最新文章

  1. 方差协方差以及协方差矩阵
  2. Win64 驱动内核编程-29.强制解锁文件
  3. 【控制】《多智能体系统的动力学分析与设计》徐光辉老师-第6章-基于间歇控制的非线性多智能体系统的多一致
  4. 前端怎么导出表格_前端如何导出表格数据
  5. python动态图片转字符画_GIF动态图,视频?都能用Python转换成字符画图像
  6. 深度学习的一些经验总结和建议| To do v.s Not To Do
  7. Shell-8--数值运算及处理
  8. java/android 做题中整理的碎片小贴士(12)
  9. 万字详解Lambda、Stream和日期
  10. JavaScript 和 Java 有关系吗?
  11. iOS开发——高级篇——iOS开发之网络安全密码学
  12. http web 返回码概念
  13. 【故障处理】CRS-1153错误处理
  14. maven自动部署到tomcat9
  15. Word参考文献交叉引用——连续多项引用
  16. java8收费_java8为什么要收费?
  17. 中文地址翻译成英文实例
  18. Introduction的Advice的实现
  19. IC卡和ID卡初步了解
  20. 原神换服报错(二级地址解析失败),换服失败,无反应

热门文章

  1. [Java工具] 邮件发送工具
  2. Radware LP 增加线路接口操作
  3. 《Python分布式计算》 第8章 继续学习 (Distributed Computing with Python)
  4. 即将改变软件开发的5个Java9新特性
  5. 日均数据量千万级,MySQL、TiDB 两种存储方案的落地对比
  6. SQL Server -- 如何书写优雅、漂亮的SQL脚本?
  7. ProtocolBuffer for Objective-C 运行环境配置(真正测试过的)
  8. 模式窗口showModalDialog的用法总结
  9. 一键自动化部署web架构
  10. ERROR 程序出错,错误原因:'bytes' object has no attribute 'read'