道不可言 發表於 2022-5-7 09:08:00

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

<p>protobuf数据类型与delphi数据类型映射</p>
<p>首先说明一下,在许多文档里面也把“结构”叫做“model(模型)”,在本文,我们统一叫做“结构”。结构,在C系语言用关键字“struct”表示,在pascal语言用“record”表示,在protobuf用"message"表示。</p>
<p><strong>protocol buffers是什么?</strong></p>
<p>是一种结构数据序列化方法。<br>定义数据的结构 &gt; 生成的源代码(.proto文件) -&gt; 在数据流中&amp;各种语言进行编写, 读取结构数据。</p>
<p>是google gRPC数据序列的核心部件。google gRPC是业界最流行的一种IDL(接口描述语言)。</p>
<p><strong>如何使用?</strong></p>
<ol>
<li><strong>定义.proto文件</strong>, 来定义你的数据结构;(消息格式文件)中定义 protocol buffer message 类型, 来指定你想如何对序列化信息进行结构化。 message 包含name-value对, 可嵌套。缺省默认就设置为 optional。</li>






</ol>
<p><strong>为什么要用.proto文件来定义结构?</strong></p>
<p>protocol buffers与具体开发语言无关,正因为与开发语言无关,所有的主流语言都支持它(中立的,大家都支持),用.proto文件定义的结构,开发语言都提供有工具来自动翻译为自己语言能识别的结构代码,</p>
<p>所以用.proto文件定义的结构的数据序列、还原,支持跨语言、跨平台。</p>
<p>反之,如果,我们向其他语言开发者提供PASCAL的RECORD结构文件,一个他们可能会看不懂,二个其他语言可能没有工具来自动翻译。</p>
<p>注:</p>
<p>message xxx {<br>// 字段规则:required -&gt; 字段只能也必须出现 1 次<br>// 字段规则:optional -&gt; 字段可出现 0 次或1次<br>// 字段规则:repeated -&gt; 字段可出现任意多次(包括 0)<br>// 类型:int32、int64、sint32、sint64、string、32-bit ....<br>// 字段编号:0 ~ 536870911(除去 19000 到 19999 之间的数字)<br>字段规则 类型 名称 = 字段编号;<br>}</p>
<p>1) message 定义中的每个字段都有唯一编号。<br>2) 单个 .proto 文件中定义多种 message 类型<br>3) message内可嵌套message<br>4) .proto 文件添加注释,可以使用 C/C++ 语法风格的注释 // 和 /* ... */</p>
<p>RuKuDan.proto</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">//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;        //操作员
}
</pre>
</div>
<p>  </p>
<p>2.<strong>使用ProtoBufCodeGen工具进行翻译,生成pascal语言的代码</strong>。<br>我们在 .proto 文件中定义了数据结构,这些数据结构是面向开发者和业务程序的,并不面向存储和传输。<br>当需要把这些数据进行存储或传输时,就需要将.proto 文件中定义的数据结构翻译为符合所使用开发语言语法的代码。我们提供全自动翻译工具。<br><img src="https://img2022.cnblogs.com/blog/368779/202206/368779-20220622081003094-1511231215.png"></p>
<p>pbRuKuDanMessages.pas</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">{ Unit pbRuKuDanMessages.pas }
{ Generated from RuKuDan.proto }
{ Package RuKuDan }

unit pbRuKuDanMessages;

interface

uses Grijjy.ProtocolBuffers, SysUtils;

{ TRuKuDan1Record }

type
TRuKuDan1Record = record
    DanHao : String;
    CangKu : String;
    GongYingShang : String;
    ShiJian : Int64;
    CaoZuoYuan : String;
    BeiZhu : String;
end;

{ TRuKuDan2Record }

type
TRuKuDan2Record = record
    DanHao : String;
    HangHao : String;
    ShangPing : String;
    JiaGe : Double;
    ShuLiang : Double;
    JinE : Double;
end;

{ TDynArrayRuKuDan2Record }

type
TDynArrayRuKuDan2Record = array of TRuKuDan2Record;

{ TRuKuDan2sRecord }

type
TRuKuDan2sRecord = record
    Rkds : TDynArrayRuKuDan2Record;
end;

{ TRuKuDanRecord }

type
TRuKuDanRecord = record
    Rkd1 : TRuKuDan1Record;
    Rkd2 : TRuKuDan2sRecord;
end;

{ TRkdChaXunRecord }

type
TRkdChaXunRecord = record
    KsShiJian : Int64;
    JzShiJian : Int64;
    CaoZuoYuan : String;
end;

implementation

end.</pre>
</div>
<p>&nbsp;3.delphi对pbRuKuDanMessages.pas定义的结构进行读、写。</p>
<p><strong>&nbsp;基于结构的二进制protobuf数据序列</strong></p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function goodsQry(url: string; body: tbytes): tbytes;
var
db: tdb;
pool: tdbpool;
serial: TgoProtocolBuffer;
arr: tarray&lt;string&gt;;
dw: TUnitsRecord;
sp: TGoodsRecord;
sps: pbGoodsMessages.TGoodsArrRecord;
i: integer;
err: TresRecord;
begin
serial := TgoProtocolBuffer.Create;
try
    try
      arr := url.Split(['/']);
      pool := GetDBPool(arr);
      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.Goodsid := db.qry.FieldByName('goodsid').AsString;
      sps.Goodss.Goodsname := db.qry.FieldByName('goodsname').AsString;
      inc(i);
      db.qry.Next;
      end;
      Result := serial.Serialize&lt;pbGoodsMessages.TGoodsArrRecord&gt;(sps);
    except
      on E: Exception do
      begin
      err.ok := False;
      err.err := e.Message;
      Result := serial.Serialize&lt;TresRecord&gt;(err);
      end;
    end;
finally
    pool.Unlock(db);
    serial.Free;
end;
end;
</pre>
</div>
<p><strong>基于结构的JSON数据序列</strong></p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">function goodsQry(url: string; body: rawbytestring): string;
var
db: tdb;
pool: tdbpool;
arr: tarray&lt;string&gt;;
serial: TJsonSerializer;
sp: TGoodsrecord;
sps: TGoodsArrRecord;
i: Integer;
err: TResRecord;
begin
serial := TJsonSerializer.Create;
try
    try
      arr := url.Split(['/']);
      pool := GetDBPool(arr);
      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 := sp;
      inc(i);
      db.qry.Next;
      end;
      Result := serial.Serialize&lt;TGoodsArrRecord&gt;(sps);
    except
      on E: Exception do
      begin
      err.ok := False;
      err.err := e.Message;
      Result := serial.Serialize&lt;TResRecord&gt;(err);
      end;
    end;
finally
    pool.Unlock(db);
    serial.Free;
end;
end;
</pre>
</div>
<p> 从上面的代码可以看出,基于结构,既可以用于二进制(PROTBUF)序列,也可以用于明文(JSON)序列。 使用DELPHI的泛型结构,编程非常方便。</p>
<p><strong>&nbsp;protobuf数据类型:</strong></p>
<p>double:&nbsp;浮点数</p>
<p>float:&nbsp;单精度浮点</p>
<p>int32:&nbsp;int类型,使用可变长编码,编码负数不够高效,如果有负数那么使用sint32</p>
<p>sint32:&nbsp;int类型,使用可变长编码,&nbsp;有符号的整形,比通常的int32高效;</p>
<p>uint32:&nbsp;无符号整数使用可变长编码方式;</p>
<p>int64 long&nbsp;long&nbsp;,&nbsp;使用可变长编码方式。编码负数时不够高效——如果有负数,可以使用sint64;</p>
<p>sint64 long&nbsp;long&nbsp;&nbsp;使用可变长编码方式。有符号的整型值。编码时比通常的int64高效;</p>
<p>uint64:&nbsp;无符号整数使用可变长编码方式;</p>
<p>fixed32 : 总是4个字节。如果数值总是比总是比2^28大的话,这个类型会比uint32高效。</p>
<p>fixed64: 总是8个字节。如果数值总是比总是比2^56大的话,这个类型会比uint64高效。</p>
<p>sfixed32: 总是4个字节。</p>
<p>sfixed64: 总是8个字节。</p>
<p>bool:bool值</p>
<p>string: 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。</p>
<p>bytes:&nbsp;可能包含任意顺序的字节数据。类似java的ByteString以及&nbsp;c++ string。</p>
<p><strong>关于TDateTime类型</strong></p>
<p>protobuf没有TDateTime类型,榔个办?</p>
<p>解决方法是用时间戳,使用int64</p>
<p>uses dateutils;</p>
<p>DateTimeToUnix(),将TDateTime转换为时间戳<br>UnixToDateTime(),将时间戳转换为TDateTime</p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:{咏南中间件},转载请注明原文链接:https://www.cnblogs.com/hnxxcxg/p/16241068.html</p><br><br>
来源:https://www.cnblogs.com/hnxxcxg/p/16241068.html
頁: [1]
查看完整版本: protobuf数据类型与delphi数据类型映射