IDocList/IDocDict JSON for Delphi and FPC

打印 上一主题 下一主题

主题 910|帖子 910|积分 2730

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中如许写:
  1. var
  2.   list: IDocList;
  3.   dict: IDocDict;
  4.   v: variant;
  5.   i: integer;
  6. begin  
  7.   // 从项目创建一个新的列表/数组
  8.   list := DocList([1, 2, 3, 'four', 1.0594631]); // 默认情况下允许双精度值
  9.   // 遍历列表
  10.   for v in list do
  11.     Listbox1.Items.Add(v); // 将变量转换为字符串
  12.   // 或列表的一个子范围(使用类似Python的负索引)
  13.   for i in list.Range(0, -3) do
  14.     Listbox2.Items.Add(IntToStr(i)); // [1, 2] 作为整数
  15.   // 搜索某些元素的存在
  16.   assert(list.Exists(2));
  17.   assert(list.Exists('four'));
  18.   // 从JSON中获取一个对象列表,其中包含一个入侵者
  19.   list := DocList('[{"a":0,"b":20},{"a":1,"b":21},"to be ignored",{"a":2,"b":22}]');
  20.   // 枚举所有对象/字典,忽略非对象元素
  21.   for dict in list.Objects do
  22.   begin
  23.     if dict.Exists('b') then
  24.       ListBox2.Items.Add(dict['b']);
  25.     if dict.Get('a', i) then
  26.       ListBox3.Items.Add(IntToStr(i));
  27.   end;
  28.   // 删除一个元素
  29.   list.Del(1);
  30.   assert(list.Json = '[{"a":0,"b":20},"to be ignored",{"a":2,"b":22}]');
  31.   // 提取一个元素
  32.   if list.PopItem(v, 1) then
  33.     assert(v = 'to be ignored');
  34.   // 转换为JSON字符串
  35.   Label1.Caption := list.ToString;
  36.   // 显示 '[{"a":0,"b":20},{"a":2,"b":22}]'
  37. end;
复制代码
以及更多高级功能,如排序、搜索和表达式过滤:
[code]var  v: variant;  f: TDocDictFields;  list, list2: IDocList;  dict: IDocDict;begin  list := 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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表