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