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
結構的具體實現細節。 - 序列化、反序列化和轉碼功能可能依賴於外部庫或自定義實現。