protobuf数据类型与delphi数据类型映射

protobuf数据类型与delphi数据类型映射

首先说明一下,在许多文档里面也把“结构”叫做“model(模型)”,在本文,我们统一叫做“结构”。结构,在C系语言用关键字“struct”表示,在pascal语言用“record”表示,在protobuf用"message"表示。

protocol buffers是什么?

是一种结构数据序列化方法。
定义数据的结构 > 生成的源代码(.proto文件) -> 在数据流中&各种语言进行编写, 读取结构数据。

是google gRPC数据序列的核心部件。google gRPC是业界最流行的一种IDL(接口描述语言)。

如何使用?

  1. 定义.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

 

posted @ 2022-05-07 09:08  delphi中间件  阅读(729)  评论(0编辑  收藏  举报