protobuf数据类型与delphi数据类型映射
protobuf数据类型与delphi数据类型映射
首先说明一下,在许多文档里面也把“结构”叫做“model(模型)”,在本文,我们统一叫做“结构”。结构,在C系语言用关键字“struct”表示,在pascal语言用“record”表示,在protobuf用"message"表示。
protocol buffers是什么?
是一种结构数据序列化方法。
定义数据的结构 > 生成的源代码(.proto文件) -> 在数据流中&各种语言进行编写, 读取结构数据。
是google gRPC数据序列的核心部件。google gRPC是业界最流行的一种IDL(接口描述语言)。
如何使用?
- 定义.proto文件, 来定义你的数据结构;(消息格式文件)中定义 protocol buffer message 类型, 来指定你想如何对序列化信息进行结构化。 message 包含name-value对, 可嵌套。缺省默认就设置为 optional。
为什么要用.proto文件来定义结构?
protocol buffers与具体开发语言无关,正因为与开发语言无关,所有的主流语言都支持它(中立的,大家都支持),用.proto文件定义的结构,开发语言都提供有工具来自动翻译为自己语言能识别的结构代码,
所以用.proto文件定义的结构的数据序列、还原,支持跨语言、跨平台。
反之,如果,我们向其他语言开发者提供PASCAL的RECORD结构文件,一个他们可能会看不懂,二个其他语言可能没有工具来自动翻译。
注:
message xxx {
// 字段规则:required -> 字段只能也必须出现 1 次
// 字段规则:optional -> 字段可出现 0 次或1次
// 字段规则:repeated -> 字段可出现任意多次(包括 0)
// 类型:int32、int64、sint32、sint64、string、32-bit ....
// 字段编号:0 ~ 536870911(除去 19000 到 19999 之间的数字)
字段规则 类型 名称 = 字段编号;
}
1) message 定义中的每个字段都有唯一编号。
2) 单个 .proto 文件中定义多种 message 类型
3) message内可嵌套message
4) .proto 文件添加注释,可以使用 C/C++ 语法风格的注释 // 和 /* ... */
RuKuDan.proto
//protobuf模板文件
syntax="proto3";
package RuKuDan;
//入库单 主表
message RuKuDan1 {
string DanHao = 1; //单号
string CangKu = 2; //仓库
string GongYingShang = 3; //供应商
int64 ShiJian = 4; //时间
string CaoZuoYuan = 5; //操作员
string BeiZhu = 6; //备注
}
//入库单 明细
message RuKuDan2 {
string DanHao = 1; //单号
string HangHao = 2; //行号
string ShangPing = 3; //商品
double JiaGe = 4; //价格
double ShuLiang = 5; //数量
double JinE = 6; //金额
}
//入库单 明细数组
message RuKuDan2s {
repeated RuKuDan2 rkds = 1;
}
//入库单
message RuKuDan {
RuKuDan1 rkd1 = 1;
RuKuDan2s rkd2 = 2;
}
//查询条件
message rkdChaXun {
int64 ksShiJian = 1; //开始时间
int64 jzShiJian = 2; //截止时间
string CaoZuoYuan = 3; //操作员
}
2.使用ProtoBufCodeGen工具进行翻译,生成pascal语言的代码。
我们在 .proto 文件中定义了数据结构,这些数据结构是面向开发者和业务程序的,并不面向存储和传输。
当需要把这些数据进行存储或传输时,就需要将.proto 文件中定义的数据结构翻译为符合所使用开发语言语法的代码。我们提供全自动翻译工具。
pbRuKuDanMessages.pas
{ Unit pbRuKuDanMessages.pas }
{ Generated from RuKuDan.proto }
{ Package RuKuDan }
unit pbRuKuDanMessages;
interface
uses Grijjy.ProtocolBuffers, SysUtils;
{ TRuKuDan1Record }
type
TRuKuDan1Record = record
[Serialize(1)] DanHao : String;
[Serialize(2)] CangKu : String;
[Serialize(3)] GongYingShang : String;
[Serialize(4)] ShiJian : Int64;
[Serialize(5)] CaoZuoYuan : String;
[Serialize(6)] BeiZhu : String;
end;
{ TRuKuDan2Record }
type
TRuKuDan2Record = record
[Serialize(1)] DanHao : String;
[Serialize(2)] HangHao : String;
[Serialize(3)] ShangPing : String;
[Serialize(4)] JiaGe : Double;
[Serialize(5)] ShuLiang : Double;
[Serialize(6)] JinE : Double;
end;
{ TDynArrayRuKuDan2Record }
type
TDynArrayRuKuDan2Record = array of TRuKuDan2Record;
{ TRuKuDan2sRecord }
type
TRuKuDan2sRecord = record
[Serialize(1)] Rkds : TDynArrayRuKuDan2Record;
end;
{ TRuKuDanRecord }
type
TRuKuDanRecord = record
[Serialize(1)] Rkd1 : TRuKuDan1Record;
[Serialize(2)] Rkd2 : TRuKuDan2sRecord;
end;
{ TRkdChaXunRecord }
type
TRkdChaXunRecord = record
[Serialize(1)] KsShiJian : Int64;
[Serialize(2)] JzShiJian : Int64;
[Serialize(3)] CaoZuoYuan : String;
end;
implementation
end.
3.delphi对pbRuKuDanMessages.pas定义的结构进行读、写。
基于结构的二进制protobuf数据序列
function goodsQry(url: string; body: tbytes): tbytes;
var
db: tdb;
pool: tdbpool;
serial: TgoProtocolBuffer;
arr: tarray<string>;
dw: TUnitsRecord;
sp: TGoodsRecord;
sps: pbGoodsMessages.TGoodsArrRecord;
i: integer;
err: TresRecord;
begin
serial := TgoProtocolBuffer.Create;
try
try
arr := url.Split(['/']);
pool := GetDBPool(arr[4]);
db := pool.Lock;
db.qry.Close;
db.qry.SQL.Clear;
db.qry.SQL.Text := 'select * from tgoods';
db.qry.Open;
SetLength(sps.Goodss, db.qry.RecordCount);
db.qry.First;
i := 0;
while not db.qry.Eof do
begin
sps.Goodss[i].Goodsid := db.qry.FieldByName('goodsid').AsString;
sps.Goodss[i].Goodsname := db.qry.FieldByName('goodsname').AsString;
inc(i);
db.qry.Next;
end;
Result := serial.Serialize<pbGoodsMessages.TGoodsArrRecord>(sps);
except
on E: Exception do
begin
err.ok := False;
err.err := e.Message;
Result := serial.Serialize<TresRecord>(err);
end;
end;
finally
pool.Unlock(db);
serial.Free;
end;
end;
基于结构的JSON数据序列
function goodsQry(url: string; body: rawbytestring): string;
var
db: tdb;
pool: tdbpool;
arr: tarray<string>;
serial: TJsonSerializer;
sp: TGoodsrecord;
sps: TGoodsArrRecord;
i: Integer;
err: TResRecord;
begin
serial := TJsonSerializer.Create;
try
try
arr := url.Split(['/']);
pool := GetDBPool(arr[4]);
db := pool.Lock;
db.qry.Close;
db.qry.SQL.Clear;
db.qry.SQL.Text := 'select * from tgoods';
db.qry.Open;
SetLength(sps.Goodss, db.qry.RecordCount);
db.qry.First;
i := 0;
while not db.qry.Eof do
begin
sp.goodsid := db.qry.FieldByName('goodsid').AsString;
sp.goodsname := db.qry.FieldByName('goodsname').AsString;
sps.Goodss[i] := sp;
inc(i);
db.qry.Next;
end;
Result := serial.Serialize<TGoodsArrRecord>(sps);
except
on E: Exception do
begin
err.ok := False;
err.err := e.Message;
Result := serial.Serialize<TResRecord>(err);
end;
end;
finally
pool.Unlock(db);
serial.Free;
end;
end;
从上面的代码可以看出,基于结构,既可以用于二进制(PROTBUF)序列,也可以用于明文(JSON)序列。 使用DELPHI的泛型结构,编程非常方便。
protobuf数据类型:
double: 浮点数
float: 单精度浮点
int32: int类型,使用可变长编码,编码负数不够高效,如果有负数那么使用sint32
sint32: int类型,使用可变长编码, 有符号的整形,比通常的int32高效;
uint32: 无符号整数使用可变长编码方式;
int64 long long , 使用可变长编码方式。编码负数时不够高效——如果有负数,可以使用sint64;
sint64 long long 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效;
uint64: 无符号整数使用可变长编码方式;
fixed32 : 总是4个字节。如果数值总是比总是比2^28大的话,这个类型会比uint32高效。
fixed64: 总是8个字节。如果数值总是比总是比2^56大的话,这个类型会比uint64高效。
sfixed32: 总是4个字节。
sfixed64: 总是8个字节。
bool:bool值
string: 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。
bytes: 可能包含任意顺序的字节数据。类似java的ByteString以及 c++ string。
关于TDateTime类型
protobuf没有TDateTime类型,榔个办?
解决方法是用时间戳,使用int64
uses dateutils;
DateTimeToUnix(),将TDateTime转换为时间戳
UnixToDateTime(),将时间戳转换为TDateTime
本文来自博客园,作者:{咏南中间件},转载请注明原文链接:https://www.cnblogs.com/hnxxcxg/p/16241068.html

浙公网安备 33010602011771号