Google序列化-Protobuf的语法与实际使用案例

基础

0.1 序列化

  • 序列化
    对象转换为字节序列(字符串)的过程
  • 反序列化
    字节序列 恢复为 对象的过程
struct User {
	string name;
	int age;
	string sex;
};

User u1 = {'Jack', 25, 'Male'};

// 序列化
string str = "Jack\n18\nMale";

当需要将数据存储,保存对象状态;或者网络进行传输数据时,不能传输整个对象;

Socket编程发送与接收就是序列化与反序列化

实现序列化的手段:
Protobuf, [[JSON]], [[XML]]

0.2 Protobuf

全称 : Protocol Buffers 是Google的 语言无关、平台无关、可扩展的 二进制数据序列化协议
可以用于通信协议、数据存储等场景的 结构化数据 存储传输

proto 比 xml 更小更快更简单
proto 可以定义数据的结构、会自动生成源代码;更新数据结构,不会破坏原有结构编译的已部署程序

0.3 使用简述

编写.proto文件,定义数据结构和属性;
protoc编译器编译.proto,生成接口;
在目标代码中调用接口;

Proto结构

1.1 语法

syntax = "proto3"; // 指定 proto3 语法

package example;  // 包名 (命名空间既视感)

message Person {
	string name = 1; // 字段类型 + 字段名称 + 唯一编号tag 
	int32 id = 2;
	string email = 3;
	repeated string phones = 4;
}

一个字段的完整是:
<规则> <类型> <名> = <编号>;

1.2 类型 与 规则

  • 字段类型
int32 string bool bytes enum message
  • 字段规则
optional // 可选
repeated // 重复  数组
oneof   // 多选一 [[Protobuf#1.4 oneof 多态]]
  • 字段编号
    唯一编号; 1~15只占用1字节

1.3 嵌套

实现复杂结构

message Base {
	string base_field = 1;
}

message Derived {
	Base base = 1;
	string derived_field = 2;
}

1.4 oneof 多态

可以包含多个类型字段,但是同时只能有一个被设置
多选一需求,不同字段之间是互斥的

message Event {
	oneof event_type {
		ClickEvent click = 1;
		KeyPressEvent keypress = 2;
	}
}

message PersonInfo {
	string name = 1;
	oneof contact {
		string qq = 2;
		string wx = 3; 
	}
}

然后在代码中的具体设置

PersonInfo person;
person.set_qq("12987");

person.set_wx("wx_23816"); // 这个时候会 覆盖 qq字段 

编译与使用

2.1 编译器 protoc

编写好.proto文件后,利用protoc编译器生成目标语言代码;

protoc --cpp_out=. person.proto

protoc --python_out=. person.proto

protoc --go_out=. person.proto

编译后会生成对应代码
python : persn_pb2.py
go : person.pb.go
c : person.pb.ccperson.pb.h

这种还挺常见,例如QT的.ui会生成ui_xxxx.h

2.2 使用编译后文件

进行序列化

#include "person.pb.h"

// 进行序列化
Person person;
// 构造 赋值
person.set_name( "Jack" );
person.set_id( 65 );
person.set_email( "a@bb.com" );

std::string serialized = person.SerializeToString();

反序列化

Person parsed_person;
parsed_person.ParseFromString( serialized );

2.3 提供的一些默认方法

set_,has_,clear_
mutable_
add_, _size

值操作

  • 设置
person.set_name('Jack');
  • 获取
std::string person_name = person.name();
  • 存在性检查
if ( person.has_email() ) {
	// ...
}
  • 清除
person.clear_name();

可变指针 mutable

.proto文件内容

message Address {
	string city = 1;
}
message Person{
	// ...
	Address address = 2;
}

具体使用(C++为例)

Address* addr = person.mutable_address();

addr->set_city("Beijing");

Repeated 字段操作

字段定义.proto

message Exam {
	// ...
	repeated int32 scores = 3;
}

repeated的操作(C++为例)

// 添加
exam.add_scores(90);
exam.add_scores(68);

// 获取
int score_0 = exam.scores(0);
int score_2 = exam.scores(2);

// 修改
exam.mutable_scores(0)->push_back(95);

// size
int size = exam.scores_size();

交换与合并

// 交换 swap
Person p1, p2;

p1.Swap(&p2);

// 合并 merge
p1.MergeFrom(p2);

合并的话,p1字段内容会被p2覆盖或者追加

序列化对比

#proto #xml #json
[[JSON]] [[XML]]

protojsonxml
格式二进制文本 键值对文本 标签
可读性低(需要解析)高(感觉不如json)
传输效率体积小,序列化快体积较大,解析慢体积大,解析慢
类型支持强类型(预定义)
场景高性能通信
大数据传输
Web API
配置文件
数据交换
兼容旧系统
复杂文档

三个代码例子

proto

message Order {
  int64 order_id = 1;
  repeated Product products = 2; // 强类型,需要定义 // 数组
  float total_price = 3;
  Status status = 4; // 强类型,需要定义

  enum Status { // 枚举 status
    PENDING = 0;
    SHIPPED = 1;
    DELIVERED = 2;
  }

  message Product { // 嵌套类型
    string name = 1;
    int32 quantity = 2;
    float price = 3;
  }
}

json

"order" : {
	"order_id" : 1,
	"products" : [
		{"name" : "mouse", "quantity" : 2, "price" : 21.5},
		{"name" : "keyboard", "quantity" : 1, "price" : 52.49}
	],
	"total_price" : 95.49,
	"status" : "PENDING"
}

xml

<Order>
  <OrderID>1001</OrderID>
  <Products>
    <Product>
      <Name>Laptop</Name>
      <Quantity>1</Quantity>
      <Price>999.99</Price>
    </Product>
    <Product>
      <Name>Mouse</Name>
      <Quantity>2</Quantity>
      <Price>25.50</Price>
    </Product>
  </Products>
  <TotalPrice>1050.99</TotalPrice>
  <Status>PENDING</Status>
</Order>

通信

流程

  • 定义接口: 设计.proto
  • 生成代码: protoc 编译成目标语言
  • 传输数据: 二进制格式传输 (HTTP/2, gRPC)
  • 序列化/反序列化: 发送方序列化,接收方反序列化

场景

gRPC通信: 基于HTTP/2 和 protobuf 的高性能 RPC 框架

常用于 后端之间 的通信

前后端

前端(如浏览器),使用JavaScript,就将proto转为js

npm install -g protobufjs
pbjs -t static-module -w commonjs -o data_pb.js data.proto

再进行反序列化 和 序列化操作,传回序列化后的数据到后端

不展示代码

  • 通信方式
    • HTTP API: fetch 发送二进制数据
    • WebSocket: 直接传输二进制流
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值