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