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
结构的具体实现细节。 - 序列化、反序列化和转码功能可能依赖于外部库或自定义实现。