兜蛇 发表于 3 天前

IDocList/IDocDict JSON for Delphi and FPC

IDocList/IDocDict JSON for Delphi and FPC

【英文原文】
多年来,我们的开源 mORMot 框架提供了多种方法来处理在运行时定义的数组/对象文档的任意组合,例如通过 JSON,具有许多功能和非常高的性能。

我们的 TDocVariant自定义变体类型是处理这类无模式数据的一种强大方式,但一些用户觉得它有些令人困惑。
因此,我们围绕它开发了一套新的接口定义,以简化其使用,同时不牺牲其功能。我们围绕Python列表和字典对它们进行了建模,这已被证明是可行的——当然,也做了一些扩展。
TDocVariant的优缺点

多年来,我们的 TDocVariant可以存储任何基于JSON/BSON的文档内容,即:

[*]面向对象文档的名/值对——在内部被标识为 dvObject子类型;
[*]面向数组文档的值数组(包括嵌套文档)——在内部被标识为 dvArray子类型;
[*]通过嵌套 TDocVariant实例,可以实现上述两者的任意组合。
每个 TDocVariant实例也是一个自定义的变体类型:

[*]因此,您可以将它存储或转换为变体变量;
[*]您可以使用后期绑定来访问其对象属性,这在现代Pascal的严格世界中有点像魔术;
[*]Delphi IDE(和Lazarus 3.x)调试器对其有原生支持,因此可以将变体内容显示为JSON;
[*]如果您在任何类或记录中定义了变体类型,我们的框架将识别 TDocVariant内容,并将其序列化和反序列化为JSON,例如在其ORM、SOA或Mustache/MVC部分中。
这种强大功能也带来了一些缺点:

[*]在变体和其 TDocVariantData记录之间切换可能很棘手,有时需要一些令人困惑的指针引用;
[*]每个 TDocVariant实例都可以用作对其他数据的弱引用,或者维护其自身的内容——在某些极端情况下,不正确的使用可能会导致内存泄漏或GPF问题;
[*]TDocVariant可以是对象/字典或数组/列表,因此找到正确的方法可能很困难,或者在运行时引发异常;
[*]它从一个简单的存储发展成了一个完整的内存引擎,因此高级功能通常被低估;
[*]TDocVariantData记录与大多数Delphi/FPC用户所习惯的类系统相去甚远;
[*]默认情况下,不解析双精度值——只解析货币值——如果你不想损失任何精度,这是有意义的,但也被发现会造成混淆。
抱怨够了。
我们只需让它变得更好。
引入IDocList和IDocDict接口
我们引入了两个高级封装接口类型:

[*]IDocList(或其别名IDocArray)用于存储元素列表;
[*]IDocDict(或其别名IDocObject)用于存储键值对字典。
接口方法和命名遵循通常的Python列表和字典,并在安全且专用于类的IDocList和IDocDict类型中封装它们自己的TDocVariant存储。
您可能会在现代Delphi中这样写:
var
list: IDocList;
dict: IDocDict;
v: variant;
i: integer;
begin
// 从项目创建一个新的列表/数组
list := DocList(); // 默认情况下允许双精度值

// 遍历列表
for v in list do
    Listbox1.Items.Add(v); // 将变量转换为字符串

// 或列表的一个子范围(使用类似Python的负索引)
for i in list.Range(0, -3) do
    Listbox2.Items.Add(IntToStr(i)); // 作为整数

// 搜索某些元素的存在
assert(list.Exists(2));
assert(list.Exists('four'));

// 从JSON中获取一个对象列表,其中包含一个入侵者
list := DocList('[{"a":0,"b":20},{"a":1,"b":21},"to be ignored",{"a":2,"b":22}]');

// 枚举所有对象/字典,忽略非对象元素
for dict in list.Objects do
begin
    if dict.Exists('b') then
      ListBox2.Items.Add(dict['b']);
    if dict.Get('a', i) then
      ListBox3.Items.Add(IntToStr(i));
end;

// 删除一个元素
list.Del(1);
assert(list.Json = '[{"a":0,"b":20},"to be ignored",{"a":2,"b":22}]');

// 提取一个元素
if list.PopItem(v, 1) then
    assert(v = 'to be ignored');

// 转换为JSON字符串
Label1.Caption := list.ToString;
// 显示 '[{"a":0,"b":20},{"a":2,"b":22}]'
end; 以及更多高级功能,如排序、搜索和表达式过滤:
varv: variant;f: TDocDictFields;list, list2: IDocList;dict: IDocDict;beginlist := DocList('[{"a":10,"b":20},{"a":1,"b":21},{"a":11,"b":20}]');// 根据嵌套对象的字段对列表/数组进行排序list.SortByKeyValue(['b', 'a']);assert(list.Json = '[{"a":10,"b":20},{"a":11,"b":20},{"a":1,"b":21}]');    // 使用条件表达式枚举列表/数组 :)for dict in list.Objects('b
页: [1]
查看完整版本: IDocList/IDocDict JSON for Delphi and FPC