mORMot2 生成和解析 JSON

mormot2 生成和解析json

本文非完全原創,本文部分內容來自博客園,作者:{詠南中間件}

前綜合示例,整個示例是建立在mORMot特有的實現模式的基礎上,非常用的序列化反序列化,但又有別於字符串拼接,據說效率極高。

unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls,
  mormot.core.perf,
  mormot.core.Data,
  mormot.core.text,
  mormot.core.json,
  mormot.core.variants,
  mormot.core.base,
  mormot.core.log ;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}

解析JSON:

  • 基礎

下面是最基本的方法,如何定位!這裏用到了 GetValueByPath 函數。

procedure TForm1.Button1Click(Sender: TObject);
//解析json
var
  js: TDocVariantData;//這個類型就是mormot利用Variant擴展的特有方案
  json: string;
begin
  json := '{"tt":"1"}';
  js.InitJSON(json); //從字符串到Variant
  Caption := js.GetValueByPath(['tt']);//定位
end;
  • 進階

通過 DocVariantData(GetValueByPath (xxx)).Value[ ] 可以訪問對於Array或者List可以訪問元素值

對於 TDocVariantData類型的變量可以通過特徵屬性函數直接訪問【U[]:RawUtf8;S[]:string;B[]:boolean;I[]:Int64;D[]:double;O[]:PDocVariantData;O_[] A[]:PDocVariantData; A_[] _[]】

(*
{
  "blockCount":3,
  "blocks":[
    {"FieldCount":1, "fields":[{"Name":"姓名", "Value":["張1", "張2","張三"]}]},
    {"FieldCount":1, "fields":[{"Name":"單位", "Value":["華2", "張2","張三"]}]},
    {"FieldCount":1, "fields":[{"Name":"單位", "Value":["華拓", "張2","張三"]}]}
  ]
}
*)
 
procedure TForm1.Button2Click(Sender: TObject);
//解析json
var
  js, js2, js3: TDocVariantData;
begin
  js.InitJSONFromFile('tt.json');
  caption := DocVariantData(js.GetValueByPath(['blocks'])).value[0]; //{"FieldCount":1, "fields":[{"Name":"姓名", "Value":["張1", "張2","張三"]}]}
  js2.InitJSON(caption);
  caption := DocVariantData(js2.GetValueByPath(['fields'])).value[0]; //{"Name":"姓名", "Value":["張1", "張2","張三"]}
  js3.InitJSON(caption);
  Caption := js3.U['Name'] + DocVariantData(js3.GetValueByPath(['Value'])).value[0]; //姓名張1
  caption := DocVariantData(DocVariantData(js.A['blocks'].Value[1]).A['fields'].Value[0]).A['Value'].Value[0]; //華2
end;

生成JSON:

(*
{"Name":"Str0","Age":0,"List":[1,"Hello",5,{"name":"詠南中間件","age":99}]}
{"Name":"Str1","Age":1,"List":[1,"Hello",5,{"name":"詠南中間件","age":99}]}
*)
procedure TForm1.Button3Click(Sender: TObject);
//生成json
var
  jo: Variant;
  i: Int64;
begin
  TDocVariant.New(jo);
  i := 0;
  while i < 2 do
  begin
    jo.Name := 'Str' + IntToStr(i);
    jo.Age := i;
    jo.List := _JSon('[1,"Hello",5,{"name":"詠南中間件","age":99}]');
    Memo1.Lines.Add(VariantSaveJSON(jo));
    inc(i);
  end;
end;
 
end.

關於TDocVariantData:

利用TDocVariantData做json解析, 就是在這個文檔(JSON文檔,文檔泛指JSON)中查找一個項目,並返回其值。

  • 如果aNameOrIndex既不是整數也不是字符串,則拋出EDocVariant異常
  • 如果Kind是dvArray且aNameOrIndex是字符串,或者Kind是dvObject且aNameOrIndex是整數,則拋出EDocVariant異常
  • 如果Kind是dvObject且aNameOrIndex是字符串,在對象屬性名稱中找不到該字符串,且Options中設置了dvoReturnNullForUnknownProperty,則拋出EDocVariant異常
  • 如果Kind是dvArray且aNameOrIndex是整數,該整數不在0到Count-1的範圍內,且Options中設置了dvoReturnNullForUnknownProperty,則拋出EDocVariant異常
  • 因此,您可以直接這樣使用:

對於數組類型的文檔:

aVariant := TDocVariant.NewArray(['one',2,3.0]);
for i := 0 to TDocVariantData(aVariant).Count-1 do
  aValue := TDocVariantData(aVariant).Value[i];

對於對象類型的文檔:

aVariant := TDocVariant.NewObject(['name','John','year',1972]);
assert(aVariant.Name=TDocVariantData(aVariant)['name']);
assert(aVariant.year=TDocVariantData(aVariant)['year']);

由於變體的執行內部實現(較慢的_DispInvoke()函數),執行以下操作會稍快一些:

aValue := TDocVariantData(aVariant).Value['name'];
// 或者
aValue := _Safe(aVariant).Value['name'];

// 而不是
aValue := aVariant.name;

當然,如果想通過索引訪問內容(通常是dvArray),使用Values[]和Names[]屬性會比使用變體索引的僞屬性更快:

with TDocVariantData(aVariant) do
for i := 0 to Count-1 do
Writeln(Values[i]); //這裏是Values

比以下代碼更快:

with TDocVariantData(aVariant) do
  for i := 0 to Count-1 do
  Writeln(Value[i]);

這又比以下代碼更快:

for i := 0 to aVariant.Count-1 do
Writeln(aVariant._(i));

此屬性將值作爲varByRef返回(就像對任何TDocVariant實例的變體後期綁定一樣),因此您可以這樣寫:

var
  Doc: TDocVariantData; // 棧上分配的變量
begin
  Doc.InitJson('{arr:[1,3]}');
  assert(Doc.Count=2);
  Doc.Value['arr'].Add(7); // 由於Doc.Value['arr']是varByRef,因此可以工作
  writeln(Doc.ToJson); // 將輸出 '{"arr":[1,3,7]}'
end;

關於TDocVariant:

當然,以下是對註釋區域的翻譯,同時我使用 Markdown 語法來高亮代碼,並去掉了行註釋標誌:


這是一個自定義的變體類型,用於存儲任何基於JSON/BSON文檔的內容。

  • 即,對象的名字/值對,或者值數組(包括嵌套文檔),這些都被存儲在 TDocVariantData內存結構中。
  • 你可以使用 _Obj()/_ObjFast(), _Arr()/_ArrFast(), _Json()/_JsonFast(), 或者 _JsonFmt()/_JsonFastFmt()函數來創建這種變體的實例。
  • 屬性訪問可以通過後期綁定來實現 - 對於較舊版本的FPC有一些限制,例如允許編寫:
TDocVariant.NewFast(aVariant);

aVariant.Name := 'John';

aVariant.Age := 35;

writeln(aVariant.Name, ' is ', aVariant.Age, ' years old');
  • 它還支持一小套僞屬性或僞方法:
aVariant._Count // 等同於 DocVariantData(aVariant).Count,訪問元素數量

aVariant._Kind  // 等同於 ord(DocVariantData(aVariant).Kind),訪問變體的類型(以整數的形式)

aVariant._JSON  // 等同於 DocVariantData(aVariant).JSON,訪問變體的JSON表示

aVariant._(i)   // 等同於 DocVariantData(aVariant).Value[i],使用索引訪問元素的值

aVariant.Value(i)   // 另一種通過索引訪問值的方式,更明確

aVariant.Value(aName) // 等同於 DocVariantData(aVariant).Value[aName],使用名字訪問元素的值

aVariant.Name(i) // 等同於 DocVariantData(aVariant).Name[i],訪問元素的名字

aVariant.Add(aItem) // 等同於 DocVariantData(aVariant).AddItem(aItem),向變體中添加一個元素

aVariant._ := aItem // 另一種添加元素的方式,語法更簡潔

aVariant.Add(aName, aValue) // 等同於 DocVariantData(aVariant).AddValue(aName, aValue),向變體中添加一個命名值對

aVariant.Exists(aName) // 等同於 DocVariantData(aVariant).GetValueIndex(aName)>=0,檢查一個名字是否存在於變體中

aVariant.Delete(i) // 等同於 DocVariantData(aVariant).Delete(i),通過索引刪除一個元素

aVariant.Delete(aName) // 等同於 DocVariantData(aVariant).Delete(aName),通過名字刪除一個元素

aVariant.NameIndex(aName) // 等同於 DocVariantData(aVariant).GetValueIndex(aName),通過名字獲取元素的索引
  • 它具有直接的JSON序列化/反序列化功能,例如:
assert(_Json('["one",2,3]')._JSON = '["one",2,3]');
  • 它具有將字符串直接轉碼爲JSON編碼字符串的功能,例如:
assert(_Json('["one",2,3]') = '["one",2,3]');

後綜合示例

弄明白mORMot的JSON,實際就是變體類型應用,他的快在於Variant本身,同時也受限於Variant有上限,估計通過優化提升不了太多。

這是一個用於存儲基於JSON/BSON文檔的內容的自定義變體類型。

功能特性:

  • 存儲對象的名字/值對或值數組(可嵌套)。
  • 通過特定的創建函數實例化。
  • 支持後期綁定的屬性訪問。
  • 提供一系列僞屬性和僞方法進行操作。
  • 允許直接的JSON序列化和反序列化。

示例代碼和註釋:

// 創建一個新的變體實例
var aVariant: TDocVariant;
TDocVariant.NewFast(aVariant);

// 設置和獲取屬性
aVariant.Name := 'John'; // 設置名字屬性
aVariant.Age := 35;     // 設置年齡屬性
writeln(aVariant.Name, ' is ', aVariant.Age, ' years old'); // 輸出屬性

// 訪問僞屬性和僞方法
writeln('Count: ', aVariant._Count);        // 元素數量
writeln('Kind: ', aVariant._Kind);          // 變體類型(整數形式)
writeln('JSON: ', aVariant._JSON);          // 變體的JSON表示
writeln('Value at index 1: ', aVariant._(1)); // 通過索引訪問元素值

// 添加元素和值
aVariant.Add('City');                      // 添加一個元素
aVariant.Add('Country', 'USA');             // 添加一個命名值對

// 檢查元素是否存在並刪除
if aVariant.Exists('Country') then
  writeln('Country exists.');
aVariant.Delete('Country');                // 通過名字刪除元素

// JSON序列化和反序列化示例
var jsonString: string;
jsonString := aVariant._JSON;               // 序列化爲JSON字符串
// ... 此處可以保存或傳輸 jsonString ...
aVariant := _Json(jsonString);             // 從JSON字符串反序列化

// 驗證JSON字符串直接轉碼功能
assert(_Json('["one",2,3]') = '["one",2,3]');

注意:

  • 實際使用時,TDocVariant 類型和相關方法需要根據您的具體實現或第三方庫進行定義和實現。
  • 僞屬性和僞方法的實現取決於底層 DocVariantData結構的具體實現細節。
  • 序列化、反序列化和轉碼功能可能依賴於外部庫或自定義實現。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章