【Protobuf】Protobuf中的Message语法规范

Protobuf快速使用 Java版、Python版

  参考上一篇:点击查看

基本语法

syntax = "proto3";

message MessageName {
    
    
    FieldType fieldName = fieldNumber;
}

  syntax指定使用的Protobuf版本。在这个示例中,使用的是Protobuf 3版本。

  代码定义了一个Message,名称为MessageName,可以根据需要自定义Message的名称。

  在Message中定义一个或者多个字段,FieldType是字段的数据类型,可以是基本类型(如int32stringbool等)或其他定义的Message类型。fieldName是字段的名称,可以根据需求自定义。fieldNumber是字段的唯一标识号,用于在消息的二进制编码中标识字段。

  例如:

syntax = "proto3";

message Email{
    
    
    int32 id = 1;
    string name = 2;
    string emails = 3;
}

标识号

  在Protobuf中,每个字段都需要一个唯一的标识号(field number),用于在消息的二进制编码中标识该字段。标识号的作用是确保在消息的编解码过程中能够准确地识别每个字段。

  以下是关于标识号的一些重要点:

  标识号是一个正整数。
  标识号的范围是1到2^29 - 1(Protobuf 3版本中是1到536,870,911)。
  在同一个Message中,每个字段的标识号必须是唯一的。
  标识号的选择是自由的,但应保持一致性和稳定性,避免频繁变更标识号。
  在编码过程中,每个字段都会与其标识号一起被序列化到二进制数据中。接收方在解码时,通过读取二进制数据中的标识号来正确识别和解析每个字段的值。

[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的字段预留一些标识号。

  在Protobuf中,包package用于对消息类型进行组织和命名空间管理。包的作用是确保消息类型的唯一性,并避免命名冲突。

  示例:

syntax = "proto3";

package com.example.mypackage;

message Email{
    
    
    int32 id = 1;
    string name = 2;
    string emails = 3;
}

  通过使用包,可以在一个大型的Protobuf项目中组织消息类型,避免不同消息类型之间的命名冲突。同时,包还可以在生成的代码中生成对应的命名空间,以便在编程语言中进行访问和引用。

选项

  java_package:单独为java定义包名字。
  java_outer_classname:单独为java定义,protobuf编译器生成的类名。

syntax = "proto3";

package com.example.mypackage;

option java_package = "com.example.mypackage";
option java_outer_classname = "Email";

message Email{
    
    
    int32 id = 1;
    string name = 2;
    string emails = 3;
}

字段类型

.proto Type Notes C++ Type Java Type Python Type[2] Go Type Ruby Type C# Type PHP Type
double   double double float float64 Float double float
float   float float float float32 Float float float
int32 使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代 int32 int int int32 Fixnum 或者 Bignum(根据需要) int integer
uint32 使用变长编码 uint32 int int/long uint32 Fixnum 或者 Bignum(根据需要) uint integer
uint64 使用变长编码 uint64 long int/long uint64 Bignum ulong integer/string
sint32 使用变长编码,这些编码在负值时比int32高效的多 int32 int int int32 Fixnum 或者 Bignum(根据需要) int integer
sint64 使用变长编码,有符号的整型值。编码时比通常的int64高效。 int64 long int/long int64 Bignum long integer/string
fixed32 总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。 uint32 int int uint32 Fixnum 或者 Bignum(根据需要) uint integer
fixed64 总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。 uint64 long int/long uint64 Bignum ulong integer/string
sfixed32 总是4个字节 int32 int int int32 Fixnum 或者 Bignum(根据需要) int integer
sfixed64 总是8个字节 int64 long int/long int64 Bignum long integer/string
bool   bool boolean bool bool TrueClass/FalseClass bool boolean
string 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 string String str/unicode string String (UTF-8) string string
bytes 可能包含任意顺序的字节数据。 string ByteString str []byte String (ASCII-8BIT) ByteString string

枚举、数组、Map

  如下代码:

syntax = "proto3";

message Staff {
    
    
    int32 id = 1;
    string name = 2;
    string email = 3;

    // 枚举示例
    enum PhoneType {
    
    
        MOBILE = 0;
        TELEPHONE = 1;
    }
    
    // 嵌套示例
    message PhoneNumber {
    
    
        string number = 1;
        PhoneType type = 2;
    }
    
    // list示例
    // 只要使用repeated标记类型定义,就表示数组类型。
    repeated PhoneNumber phone = 4;
        
    message Map {
    
    
        string key = 1;
        int32 value = 2;
    }

    // map示例
    Map map = 5;
}

消息嵌套的几种写法

  1、引用写法

// 定义Result消息
message Result {
    
    
  string url = 1;
  string title = 2;
  repeated string snippets = 3; // 字符串数组类型
}

// 定义SearchResponse消息
message SearchResponse {
    
    
  // 引用上面定义的Result消息类型,作为results字段的类型
  repeated Result results = 1; // repeated关键词标记,说明results字段是一个数组
}

  2、嵌套写法

message SearchResponse {
    
    
  // 嵌套消息定义
  message Result {
    
    
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  // 引用嵌套的消息定义
  repeated Result results = 1;
}

  3、import写法

  在开发一个项目的时候通常有很多消息定义,都写在一个proto文件,不方便维护,通常会将消息定义写在不同的proto文件中,在需要的时候可以通过import导入其他proto文件定义的消息。

   result.proto

syntax = "proto3";
// Result消息定义
message Result {
    
    
  string url = 1;
  string title = 2;
  repeated string snippets = 3; // 字符串数组类型
}

  search_response.proto

syntax = "proto3";
// 导入Result消息定义
import "result.proto";

// 定义SearchResponse消息
message SearchResponse {
    
    
  // 使用导入的Result消息
  repeated Result results = 1; 
}

消息嵌套的调用

  注意在调用消息时,要先调用嵌套消息:

  消息定义:

syntax = "proto3";

message Staff {
    
    
    int32 id = 1;
    string name = 2;
    string email = 3;

    // 枚举示例
    enum PhoneType {
    
    
        MOBILE = 0;
        TELEPHONE = 1;
    }
    
    // 嵌套示例
    message PhoneNumber {
    
    
        string number = 1;
        PhoneType type = 2;
    }
    
    // list示例
    // 只要使用repeated标记类型定义,就表示数组类型。
    repeated PhoneNumber phone = 4;
        
    message Map {
    
    
        string key = 1;
        int32 value = 2;
    }

    // map示例
    Map map = 5;
}

  调用写法:

public static void main(String[] args) {
    
    

    //  序列化
    // 创建Result的Builder
    Staffbuf.Staff.Builder staffBuilder = Staffbuf.Staff.newBuilder();
    staffBuilder.setId(1);
    staffBuilder.setName("张三丰");
    //staffBuilder.setEmail("[email protected]");

    // 构建引用消息(import message)PhoneNumber
    List list = new ArrayList();
    PhoneNumberBuf.PhoneNumber.Builder phoneBuilder =
                      PhoneNumberBuf.PhoneNumber.newBuilder();
    phoneBuilder.setType(PhoneNumberBuf.PhoneNumber.PhoneType.TELEPHONE);
    phoneBuilder.setNumber("010-12345678");
    PhoneNumberBuf.PhoneNumber phoneNumber = phoneBuilder.build();
    list.add(phoneNumber);
    phoneBuilder.clear();
    phoneBuilder.setType(PhoneNumberBuf.PhoneNumber.PhoneType.MOBILE);
    phoneBuilder.setNumber("13912345678");
    list.add(phoneBuilder.build());
    staffBuilder.addAllPhone(list);
    // 完成staff的构建
    Staffbuf.Staff zhangsanfeng = staffBuilder.build();
    // 序列化,byte[]可以被写到磁盘文件,或者通过网络发送出去。
    byte[] data = zhangsanfeng.toByteArray();
    System.out.println("serialization end.");



    // 反序列化,byte[]可以读文件或者读取网络数据构建。
    System.out.println("deserialization begin.");
    try {
    
    
        Staffbuf.Staff staff = Staffbuf.Staff.parseFrom(data);
        System.out.println(staff.getId());
        System.out.println(staff.getName());
        staff.getPhoneList().forEach(x -> System.out.println(x.toString()));
    } catch (InvalidProtocolBufferException e) {
    
    
        e.printStackTrace();
    }

}

猜你喜欢

转载自blog.csdn.net/qq_43592352/article/details/131123764
今日推荐