定义消息类型

首先让我们看一个非常简单的例子. 假设要定义搜索请求消息格式, 其中每个搜索请求都有一个查询字符串, 您感兴趣的特定结果页面以及每页的一些结果. 这是用于定义消息类型的 .proto 文件.

syntax = "proto3";

message SearchRequest {

string query = 1;

int32 page_number = 2;

int32 result_per_page = 3;

}

注意以下几点:

该文件的第一行指定使用 proto3 语法; 如果不写则使用 proto2.

一个消息对应一个字段, 一个字段对应一个数据类型.

值得注意的是: 字段类型可以是枚举或其他数据类型.

字段编号

比如 string query = 1; 字段, 1 就是字段编号(unique number).

字段编号的范围为 1 到 536,870,911. 不能使用数字 19000 到 19999, 因为它们是为 Google Protobuf 保留的.

在同一个 .proto 文件中添加多个消息类型

message SearchRequest {

string query = 1;

int32 page_number = 2;

int32 result_per_page = 3;

}

message SearchResponse {

...

}

保留字段

当定义好字段后, 在后续开发中发现某个字段根本没用.

例如 string userName = 2; 字段, 这个时候最好不要进行注释或删除.

有可能以后加载相同的旧版本, 这可能会导致数据损坏, 隐私错误等. 确保不会发生这种情况的一种方法是指定要删除的字段为保留字段.

message SubscribeReq {

reserved 2;

int32 subReqID = 1;

string userName = 2;

string productName = 3;

string address = 4;

}

顾名思义, 就是此字段会被保留可能在以后会使用此字段. 使用关键字 reserved 表示要保留字段编号为 2.

上面代码我们在生成 Java 文件的时候会出现 ubscribeReqPeoro.proto: Field "userName" uses reserved number 2 错误信息, 所以需要将 string userName = 2; 注释, 或者删除.

值得注意的是: 您不能在同一 reserved 语句中混合字段名称和字段编号. 下面是表准格式:

message Foo {

reserved 2, 15, 9 to 11;

reserved "foo", "bar";

}

指定字段规则

所指定的消息字段修饰符必须是如下之一:

singular: 一个格式良好的消息应该有0个或者1个这种字段 (但是不能超过1个).

repeated: 一个格式良好的消息中, 这种字段可以重复任意多次 (包括 0 次). 重复值的顺序会被保留.

枚举

当需要定义一个消息类型的时候, 可能想为一个字段指定某 “预定义值序列” 中的一个值.

例如, 假设要为每一个 SearchRequest 消息添加一个 corpus 字段, 而 corpus 的值可能是 UNIVERSAL, WEB, IMAGES, LOCAL, NEWS, PRODUCTS 或 VIDEO 中的一个.

其实可以很容易地实现这一点: 通过向消息定义中添加一个枚举 (enum) 并且为每个可能的值定义一个常量就可以了.

在下面的例子中, 在消息格式中添加了一个叫做 Corpus 的枚举类型——它含有所有可能的值——以及一个类型为 Corpus 的字段:

message SearchRequest {

string query = 1;

int32 page_number = 2;

int32 result_per_page = 3;

enum Corpus {

UNIVERSAL = 0;

WEB = 1;

IMAGES = 2;

LOCAL = 3;

NEWS = 4;

PRODUCTS = 5;

VIDEO = 6;

}

Corpus corpus = 4;

}

值得注意的是:

每个枚举类型必须将其第一个类型映射为 0, 这是因为:

必须有有一个 0 值, 我们可以用这个 0 值作为默认值.

这个零值必须为第一个元素, 为了兼容 proto2 语义, 枚举类的第一个值总是默认值.

另外, 枚举常量必须在 32 位整数范围内, 并且尽量不要使用负数.

你可以通过将不同的枚举常量指定为相同的值. 如果这样做你需要将 allow_alias 设置为 true.

enum EnumAllowingAlias {

option allow_alias = true;

UNKNOWN = 0;

STARTED = 1;

RUNNING = 1;

}

enum EnumNotAllowingAlias {

UNKNOWN = 0;

STARTED = 1;

// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.

}

其它消息类型

你可以将其他消息类型用作字段类型. 例如, 假设在每一个 SearchResponse 消息中包含 Result 消息, 此时可以在相同的 .proto 文件中定义一个 Result 消息类型, 然后在 SearchResponse 消息中指定一个 Result 类型的字段, 如:

message SearchResponse {

repeated Result results = 1;

}

message Result {

string url = 1;

string title = 2;

repeated string snippets = 3;

}

嵌套类型

你可以在其他消息类型中定义、使用消息类型, 在下面的例子中, Result 消息就定义在 SearchResponse 消息内, 如:

message SearchResponse {

message Result {

string url = 1;

string title = 2;

repeated string snippets = 3;

}

repeated Result results = 1;

}

如果你想在它的父消息类型的外部重用这个消息类型, 你需要以 Parent.Type 的形式使用它, 如:

message SomeOtherMessage {

SearchResponse.Result result = 1;

}

当然, 你也可以将消息嵌套任意多层, 如:

message Outer { // Level 0

message MiddleAA { // Level 1

message Inner { // Level 2

int64 ival = 1;

bool booly = 2;

}

}

message MiddleBB { // Level 1

message Inner { // Level 2

int32 ival = 1;

bool booly = 2;

}

}

}

导入定义

在上面的示例中, Result 和 SearchResponse 消息类型在同一文件中的, 如果要用作字段类型的消息类型在另一个 .proto 文件中定义, 该怎么办?

可以 .proto 通过导入来使用其他文件中的定义. 需要在文件顶部添加 import 语句:

import "myproject/other_protos.proto";

默认情况下, 使用直接导入的 .proto 文件中的定义.

但是, 有时可能需要将 .proto 文件移动到新位置. 如果直接移动文件位置可能要修改许多代码位置.

我们可以使用 import public 来解决这个问题.

实例代码:

// new.proto

// All definitions are moved here

// old.proto

//This is the proto that all clients are importing.

import public“new.proto”;

import“other.proto”;

// client.proto

import "old.proto";

//您使用old.proto和new.proto中的定义,但不使用other.proto

Maps

如果要在数据定义中创建关联映射, 协议缓冲区提供了一种方便的快捷方式语法:

map < key_type ,value_type > map_field = N ;

key_type 可以是任何整数或字符串类型.

value_type 可以是除了 map 的其他类型.

例如, 如果要创建项目映射, 其中每条 Project 消息都与字符串键相关联, 则可以像下面这样定义它:

map < string,Project > projects = 3;

值得注意的是: map 不能使用 repeated.

您可以向 .proto 文件添加 package 可选说明符, 以防止协议消息类型之间的名称冲突.

package foo.bar;

message Open { ... }

然后, 您可以在定义消息类型的字段时使用包说明符:

message Foo {

...

foo.bar.Open open = 1;

...

}

Any (任意类型)

// new.proto

syntax = "proto3";

import "google/protobuf/any.proto";

message SearchRequest {

string query = 1;

int32 page_number = 2;

repeated google.protobuf.Any result_per_page = 3;

}

编译的时候需要将 google/protobuf/any.proto 和 new.proto 文件放到一起. 要注意目录结构.

然后执行下面命令进行编译:

sudo ./protoc -I=/home/sc-ik/桌面/ --java_out=./java /home/sc-ik/桌面/*.proto

在对 result_per_page 进行赋值时, 需要用到 Any 类中的 public static Any pack(T message) 方法.

在 java 代码中, 先创建 SearchRequest 对象, 然后对其他两个属性进行赋值.

New.SearchRequest.Builder searchRequest = New.SearchRequest.newBuilder();

searchRequest.setQuery("test");

searchRequest.setPageNumber(10086);

在对 result_per_page 进行赋值时, 需要注意: pack 方法的参数类型为 com.google.protobuf.Message 接口.

生成的 java 代码中, 类是继承自 GeneratedMessageV3.

public static final class SearchRequest extends

com.google.protobuf.GeneratedMessageV3 implements

// @@protoc_insertion_point(message_implements:SearchRequest)

SearchRequestOrBuilder {

private static final long serialVersionUID = 0L;

但是如果将result_per_page 赋值为 SearchRequest, 应该怎么操作呢?

重点就是继承的 GeneratedMessageV3 抽象类, 这个抽象类又继承了 AbstractMessage 抽象类, 而 AbstractMessage 抽象类就是 com.google.protobuf.Message 接口的实现.

public abstract class AbstractMessage

// TODO(dweis): Update GeneratedMessage to parameterize with MessageType and BuilderType.

extends AbstractMessageLite implements Message {

所以我们可以使用以下方法进行赋值.

searchRequest.addResultPerPage(

com.google.protobuf.Any.pack(

searchRequest.build()

)

);

pack() 方法我个人理解为序列化, 那么和他对应的是反序列化 unpack().

// 将接收到的字节数组饭序列化.

New.SearchRequest.Builder builder2 = New.SearchRequest.newBuilder().mergeFrom(bytes1);

// 获取到 result_per_page 字段的值.

Any.Builder resultPerPageBuilder = builder2.getResultPerPageBuilder(0);

// 然后使用 unpack 将任意类型 进行反序列化, 得到想要的数据.

New.SearchRequest unpack1 = resultPerPageBuilder.build().unpack(New.SearchRequest.class);

编译

编译命令:

protoc -I=$SRC_DIR --java_out=$DST_DIR $SRC_DIR/*.proto

如下:

sudo ./protoc -I=/home/sc-ik/桌面/ --java_out=./java /home/sc-ik/桌面/*.proto

protobuf string类型_Protobuf 语言指南(proto3)相关推荐

  1. protobuf string类型_Protobuf 使用指南

    一.简介 最近在手撸 IM 系统,关于数据传输格式的选择,犹豫了下,对比了 JSON 和 XML,最后选择了 Protobuf 作为数据传输格式. 毕竟 Google 出品,必属精品

  2. java怎么输入String类型_Java语言程序设计(五)从对话框获取输入及String类型

    1.String类型java char类型只能表示一个字符,为了表示一串字符,使用成为String(字符串)的数据类型,例以下述代码将消息声明为一个字符串.ide String message = & ...

  3. protobuf string类型_Protobuf3 使用其他消息类型

    Protobuf3 使用其他消息类型 您可以使用其他消息类型作为字段类型.例如,假设您希望在每个SearchResponse消息中包含Result消息,为此,您可以在.proto中定义结果消息类型,然 ...

  4. c语言参数string类型,C语言main方法的参数打印

    c语言指针数组.字符串 有写错的地方,请帮忙纠错,感谢~ main方法的第二个参数是指针数组,存储的都是指针,所以每个元素的地址都是char **类型 字符串打印的时候,会自动截止到0字符 数组的名字 ...

  5. C++string类型与C语言字符数组的转换 std::string.c_str()函数

    语法: const char *c_str();搜索 c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同. 这是为了与c语言兼容,在c语言中没有string类型,故必须通过 ...

  6. java语言特点 字符串不变_面试必问:Java中String类型为什么设计成不可变的?

    这几天在各大平台上都看到过这样一些帖子,全都是关于String类型对象不可变的问题,当然现在也是找工作的准备时期,因此花了一部分时间对其进行整理一下. 想要完全了解String,在这里我们需要解决以下 ...

  7. c++对象回收string类型成员时coredump_本体技术视点 | 虚拟机中引用性动态语言对象模型思考...

    1 引言 Ontology 的 NeoVM 虚拟机新增加了 DCALL.HAS_KEY.KEYS 以及 VALUES 等几条新的指令.因此,基于 NeoVM 的引用性动态语言对象的设计理论上可行,这可 ...

  8. C语言中没有string类型

    C语言中没有string类型 搞懂个事的根本核心是:C语言的数组类型里,唯一和字符串沾边的就只有char型数组了. 所以,理所当然的,我们就会想到用二维字符型数组来建立--把字符串当做数组里的一个元素 ...

  9. C语言没有string类型

    C语言中没有string类型. string类型是 C++.java.VB等编程语言中的. 在java.C#中,String类是不可变的,对String类的任何改变,都是返回一个新的String类对象 ...

最新文章

  1. python调试器的功能,python调试器是什么
  2. Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition) A. Bear and Game 水题
  3. python适用场景_你真的了解Python吗?什么场景使用多线程,什么场景使用多进程?...
  4. Go Out Otherwise Shut Up
  5. ❤️ 爆肝一个月!JAVA零基础入门总结(上)❤️
  6. 信号模型噪声服从零均值高斯分布_非高斯噪声下基于分数低阶循环谱的调制识别方法...
  7. 天啦噜!在家和爱豆玩quot;剪刀石头布quot;,阿里工程师如何办到?
  8. Hibernate中Java对象的生命周期
  9. 解决luyten 启动报错:this application requires a java runtime
  10. 【MATLAB教程案例11~20总结】优化类算法matlab仿真经验和技巧总结
  11. php后台您没有权限访问该页面,登陆微信公共平台提示没有权限访问该页面请点击返回首页现象的解决办法...
  12. ISO 14229、ISO 15765、ISO 11898的区别
  13. rancher/ui 路由资源对应表
  14. JS(JavaScript)详解
  15. 为何要配置环境变量?带你一探究竟
  16. 招生考试之友2017文科理科
  17. CocosCreator之KUOKUO带你做音乐可视化
  18. Codis的原理以及部署—— CODIS (分布式 Redis 解决方案)
  19. 关于对象转json字符串的几个工具比较
  20. phpexcel部分操作

热门文章

  1. shell之判断文件是否存在
  2. C++多继承构造和析构顺序
  3. 控制x86汇编指令eip的方法
  4. Emacs进阶之M-x创建别名
  5. Android关机流程解析
  6. java list 赋值jsp,在Struts中使用JavaBean和List(多行数据)类型属性-JSP教程,Java技巧及代码...
  7. java static 对象加锁_java安全编码指南之:lock和同步的正确使用
  8. 父与子python下载不了_【求助】看父与子学习Python,里面有一个滑雪小游戏,加载不出图...
  9. sql统计表中各类型金额_产品经理市场需求旺盛的10大基础技能——第1篇读透SQL...
  10. springboot2.0 图片收集