ProtoBuf 使用分享(上)

2020/07/14 ProtoBuf

代码

相关资料

代码地址:
    https://github.com/viakiba/viakiba/tree/master/proto_demo/helloworld
资料来源:
    https://developers.google.com/protocol-buffers/docs/proto3#updating
    https://www.tizi365.com/archives/367.html

安装

进入 下载 页面,如图选择对应的操作系统的文件进行下载。

下载选择 上方的是可以自己根据需要自行编译。

我下载的是 protoc-3.12.3-win64.zip 解压到指定的文件夹 如图: 解压到指定的文件夹

并且把 bin 文件路径添加到 Path 环境变量之中。 环境变量

验证安装 cmd 输入 protoc 输出有提示即安装成功。 验证安装

编写一个HelloWorld

创建一个 person.proto

/* 多行注释 */
syntax = "proto3"; //1. 双斜杠后面可以加注释  2. 设置语法 可选 proto3 、 proto2 
package hello.world.example;// package 声明有助于防止不同项目之间的命名冲突。 

message Person {
    string name = 1; //定义一个字段 name 属性
}

编译

文件位置如图

文件位置

执行命令如下

protoc -I=C:\\Users\\89264\\Desktop\\helloworld --java_out=C:\\Users\\89264\\Desktop\\helloworld\\JavaMessageBuild C:\\Users\\89264\\Desktop\\helloworld\\message\\hello.proto

# -I 指定绝对路径
# --java_out 指定输出 java 消息的文件位置
# --python_out 指定输出 python 消息的文件位置
# 后面跟上 刚刚写好的 proto 的文件

结果如下图

文件位置

使用

创建一个 maven 工程,根文件夹选择 C:\Users\89264\Desktop\helloworld . 根文件夹

注意标记 JavaMessageBuild 为 sourceRoot 如图所示 sourceRoot

引入依赖:

    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>3.12.2</version>
    </dependency>
    // write
    String name = "hello_world";
    Hello.Person.Builder builder = Hello.Person.newBuilder();
    builder.setName(name);
    Hello.Person build = builder.build(); // Person 继承 com.google.protobuf.GeneratedMessageV3 抽象类
    byte[] bytes = build.toByteArray();
    System.out.println(Base64.getEncoder().encodeToString(bytes));

    // read
    Hello.Person builderreader = Hello.Person.parseFrom(bytes);
    String nameReader = builderreader.getName();
    System.out.println(nameReader);

code

消息定义

https://developers.google.com/protocol-buffers/docs/proto3
这里看到了一个翻译贴
https://www.tizi365.com/archives/371.html
和官网查不多 直接看就好了 不多翻译了

标识号

我们上面写的proto文件中,name属性后面跟的有一个 1 ,此处的 的 1 叫做标识号. 值得注意的是,这些标识号是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变,每个消息内唯一即可,不同的消息定义可以拥有相同的标识号。

注意:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的字段预留一些标识号。
message Hello {
  reserved 2, 15, 9 to 11; // 保留2,15,9到11这些标识号
  reserved "foo", "bar"; // 保留foo bar 这些字段名称 但是 不可同时使用保留 字段名称与标识号
}

定义 package

我们上面写的proto文件中,package 指定为 hello.world.example 则对应 文件夹 hello/world/example . 同时在声明package参数时,可以指定 java_package 与 java_outer_classname. 前者单独为java定义包名字。后者单独为java定义,protobuf编译器生成的类名。

生成指定语言的代码

--cpp_out=OUT_DIR           指定代码生成目录,生成 C++ 代码
--csharp_out=OUT_DIR        指定代码生成目录,生成 C# 代码
--java_out=OUT_DIR          指定代码生成目录,生成 java 代码
--js_out=OUT_DIR            指定代码生成目录,生成 javascript 代码
--objc_out=OUT_DIR          指定代码生成目录,生成 Objective C 代码
--php_out=OUT_DIR           指定代码生成目录,生成 php 代码
--python_out=OUT_DIR        指定代码生成目录,生成 python 代码
--ruby_out=OUT_DIR          指定代码生成目录,生成 ruby 代码
--go_out=OUT_DIR          指定代码生成目录,生成 go 代码

部分语言 需要安装插件,例如 goo语言:

安装go语言的protoc编译器插件
go get -u github.com/golang/protobuf/protoc-gen-go

注意: 安装go语言插件后需要将 $GOPATH/bin 路径加入到PATH环境变量中

数据类型

https://developers.google.com/protocol-buffers/docs/proto3#scalar

code

默认值

当解析消息时,如果编码的消息不包含特定的单一元素,则解析的对象中的相应字段被设置为该字段的默认值。 这些默认值是特定于类型的:

  • 对于字符串,默认值为空字符串。
  • 对于字节,默认值为空字节。
  • 对于bools,默认值为false。
  • 对于数值类型,默认值为零。
  • 对于枚举,默认值是第一个定义的枚举值,该值必须为0。
  • 对于消息字段,该字段未设置。 它的精确值取决于语言。 有关详细信息,请参阅生成的代码指南。
  • 重复字段的默认值为空(通常是相应语言的空列表)。

请注意,对于标量消息字段,一旦解析了消息,就无法判断字段是显式设置为默认值(例如,布尔值是否设置为false),还是根本没有设置:在定义消息类型时,您应该牢记这一点。 例如,如果您不希望某些行为在默认情况下也发生,则不要使用布尔值在设置为false时打开该行为。 还要注意,如果将标量消息字段设置为其默认值,则该值将不会在网络上序列化。 有关默认如何在生成的代码中工作的更多详细信息,请参见所选语言的生成代码指南。

枚举类型

https://developers.google.com/protocol-buffers/docs/proto3#enum
https://www.tizi365.com/archives/376.html

注意

枚举类需要一个 0 值的枚举字段.原因有两个: 1. 作为默认值 2. 兼容proto2 .

不同的枚举常量分配相同的值来定义别名 设置 option allow_alias = true

    message MyMessage1 {
        enum EnumAllowingAlias {
            option allow_alias = true;
            UNKNOWN = 0;
            STARTED = 1;
            RUNNING = 1;
        }
    }
    message MyMessage2 {
        enum EnumNotAllowingAlias {
            UNKNOWN = 0;
            STARTED = 1;
            // RUNNING = 1;  // Uncommenting this line will cause a compile error inside Google and a warning message outside.
        }
    }

repeated 数组

message Msg {
  // 只要使用repeated标记类型定义,就表示数组类型。int32 可以替换为任何类型
  repeated int32 arrays = 1;
}

Map

https://developers.google.com/protocol-buffers/docs/proto3#maps
https://www.tizi365.com/archives/383.html

注意

key_type 只能是 数字 or 字符串 
映射按键排序。 数字键按数字排序。
值的迭代排序未定义,因此您不能依赖于项目按特定顺序排列。
map字段不能同时使用repeated。

未知字段

未知类型是指数据的格式符合Protobuf的定义,但是数据中的某个/某些字段解析器无法识别的字段类型。
一般发生在proto文件有变化,新旧数据不一致的情况导致。
proto3最开始对于不能识别的数据就丢弃掉了,但是自3.5 版本后重新引入了未知字段,以匹配proto2的行为。

Search

    Table of Contents