protobuf枚举与扩展的使用方法

枚举与扩展信息

protobuf枚举字段的读写

使用protobuf时,我们可以在把枚举值定义在proto文件中,定义protobuf枚举的时候,需要注意以下几个事项:

  1. 不要把0定义成有业务语义的枚举
  2. proto2版本里,需要将0定义成一个无业务语义的枚举

这是因为proto2 与 proto3对于枚举的默认行为不同。举例来说,对于EnumType这个枚举,在程序A编译运行时,引用了版本A的proto文件。后续proto更新,EnumType这个枚举中,增加了第三个枚举值ENUM_THREE。并引用版本B的proto文件,编译运行了程序B。此时程序A和程序B使用protobuf传输数据时,就会发生异常。

syntax = "proto2";
package demo;

// 版本A:
enum EnumType {
ENUM_ONE         = 1;
ENUM_TWO         = 2;
}

// 版本B:
enum EnumType {
ENUM_ONE         = 1;
ENUM_TWO         = 2;
ENUM_THREE       = 3;
}

message EnumMessage {
    optional EnumType enum_type = 1;
}

此时程序A和程序B使用了两个不同版本的proto文件,当程序B向程序A发送一个EnumMessage,并且将enum_type字段赋值为:ENUM_THREE。当程序A收到程序B发送的二进制数据,并反序列化为EnumMessage时,由于程序A并不认识ENUM_THREE这个新增枚举,因此:

  1. 在proto2中,陌生枚举值会被反序列化对应枚举类型的第一个枚举。ENUM_THREE这个枚举值会被反序列化为EnumType的第一个枚举值——ENUM_ONE;
  2. 在proto3中,陌生枚举值会被反序列化0。ENUM_THREE这个枚举值会被反序列化为0

因此定义枚举时,最好把枚举值0定义成UNKOWN枚举

枚举的API

  1. 判断是否是有效枚举:XXXX_IsValid

例如要判断EnumType的类型是不是有效,可以使用EnumType_IsValid进行判断:

demo::EnumMessage enum_message;
if (demo::EnumType_IsValid(enum_message.enum_type())) {
    // do something
}

XXXX_IsValid函数将会根据输入的参数的值返回true或者false,如果输入的值在枚举中有定义,那么将返回true,否则返回false。在proto2中,由于通常要把枚举值0定义成未知枚举,因为XXXX_IsValid函数也会把0判断成一个有效的枚举值。

  1. 获取枚举的最大值和最小值:XXXX_MAX``````XXXX_MIN
    通过MAX和MIN函数可以获取到某个枚举类型定义的最大枚举和最小枚举值

给枚举添加扩展信息

有时候,我们需要在代码中把枚举值转义成文案,最简单的做法是像下面这样,把枚举值和文案硬编码到代码中:

std::string EnumToString(uint32_t enum_value) {
    std::map<uint32_t, std::string> enum_map = {
        { 1, "第一个枚举值"},
        { 2, "第二个枚举值"},
    }

    if (enum_map.count(enum_value)) {
        return enum_map.at(enum_value);
    }

    return "";
}

上面这样代码的可维护性比较差,在proto2中,我们可以在定义枚举值时添加扩展信息,就把对应的文案绑定到对应的枚举值中,最后使用protobuf的反射读取扩展信息中的枚举值。
我们可以这样定义一个protobuf枚举:

syntax = "proto2";
package demo;

import "google/protobuf/descriptor.proto";

// 定义扩展信息
extend google.protobuf.EnumValueOptions {
  optional string EnumDesc = 50001;              // 中文文案
}

enum EnumType {
ENUM_UNKOWN      = 0;
ENUM_ONE         = 1[(EnumDesc) = "第一个枚举值"];
ENUM_ONE         = 1[(EnumDesc) = "第二个枚举值"];
}

message EnumMessage {
    optional EnumType enum_type = 1;
}

protobuf的扩展

首先简单介绍一下protobuf扩展的使用方法:

  1. 字段,消息,枚举的拓展,各不相同:
extend google.protobuf.FileOptions {
  optional string my_file_option = 50000;
}
extend google.protobuf.MessageOptions {
  optional int32 my_message_option = 50001;
}
extend google.protobuf.FieldOptions {
  optional float my_field_option = 50002;
}
extend google.protobuf.OneofOptions {
  optional int64 my_oneof_option = 50003;
}
extend google.protobuf.EnumOptions {
  optional bool my_enum_option = 50004;
}
extend google.protobuf.EnumValueOptions {
  optional uint32 my_enum_value_option = 50005;
}
extend google.protobuf.ServiceOptions {
  optional MyEnum my_service_option = 50006;
}
extend google.protobuf.MethodOptions {
  optional MyMessage my_method_option = 50007;
}
  1. 枚举的字段编号范围:50000-99999,protobuf的官方文档中将该范围的字段编号保留给个别组织内部使用,如果要在公共应用程序中使用扩展,应该保证该扩展的编号全局唯一。并且每各类型的扩展编号是独立的,无需担心不同类型的扩展编号重合

读取枚举的拓展信息

给protobuf的枚举添加拓展信息后,可以使用反射直接读取枚举值对应的文案:

std::string EnumToString(uint32_t enum_value) {
  auto enum_descriptor = demo::EnumType_descriptor();
  auto label_descriptor = enum_descriptor->FindValueByNumber(enum_value);
  if (label_descriptor != nullptr) {
    return label_descriptor->options().GetExtension(mmpayqcmcharchiveinspectdto::EnumDesc);
  }

  return "";
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ProtobufProtocol Buffers)是一种轻量级的数据序列化格式,用于结构化数据的存储和交换。下面是使用Protobuf的一般步骤: 1. 定义消息格式:使用Protobuf语言(通常是.proto文件)定义消息结构和字段类型。例如,以下是一个简单的消息定义: ``` syntax = "proto3"; message Person { string name = 1; int32 age = 2; repeated string hobbies = 3; } ``` 2. 编译.proto文件:使用Protobuf编译器将.proto文件编译成目标语言的代码。例如,使用protoc命令行工具编译上述.proto文件: ``` protoc --proto_path=proto_directory --python_out=output_directory your_proto_file.proto ``` 这将生成一个或多个目标语言的源代码文件,用于在应用程序使用Protobuf消息。 3. 在应用程序使用消息:在目标语言的应用程序导入生成的代码,并使用消息类创建和操作消息对象。例如,在Python导入生成的代码并使用消息对象: ```python import your_proto_file_pb2 person = your_proto_file_pb2.Person() person.name = "John" person.age = 30 person.hobbies.append("programming") # 序列化为字节流 serialized_data = person.SerializeToString() # 反序列化为消息对象 deserialized_person = your_proto_file_pb2.Person() deserialized_person.ParseFromString(serialized_data) print(deserialized_person.name) # 输出: John print(deserialized_person.age) # 输出: 30 print(deserialized_person.hobbies) # 输出: ['programming'] ``` 4. 序列化和反序列化:使用消息对象的SerializeToString()方法将消息对象序列化为字节流,以便在网络传输或存储使用使用ParseFromString()方法将字节流反序列化为消息对象。 这只是Protobuf的基本使用方法Protobuf还提供了更高级的功能,如嵌套消息、枚举类型、默认值、扩展等。您可以参考Protobuf的官方文档和相关教程来深入了解更多细节和用法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值