当前位置: 首页 > 工具软件 > ObjectScript > 使用案例 >

第3集丨ObjectScript JSON 中 Iterator 和 spaAry

鲁鹏
2023-12-01

写在前面

为了与大家保持一个愉快的沟通,以及便于描述方便,本文做了一些术语简写,如下:

  • 文章宗旨:尽量不讲废话
  • 文章思路:以总结的方式编写
  • dynEnt:动态实体
  • dynObj:动态对象
  • dynAry:动态数组
  • textConstructor( 文本构造器):文字 JSON 构造函数{}[]
  • jsonStr:文本 JSON 字符串
  • * :表示重点掌握
  • -:废话可忽略
  • spaAry(稀疏数组):有些元素没有值

1. 使用 %GetNext()迭代动态实体

动态实体使用标准迭代方法 %GetNext(),该方法同时适用于对象和数组。还可以通过按顺序对每个元素进行寻址(使用 for 循环或类似结构)来循环访问数组,但这可能需要一些稀疏数组spaAry的知识,这些数组具有不包含值的元素。由于 %GetNext() 通过跳过这些元素来避免问题,因此它应该尽可能成为首选的迭代方法。

所有动态实体都提供 %GetIterator() 方法,该方法返回一个 %Iterator实例(%Iterator.Object 或% Iterator.Array ),其中包含指向dynObjdynAry成员的指针。%Iterator 对象提供了一个 %GetNext()方法来获取每个成员的键和值。

%GetNext() 方法的每次调用都会推进迭代器游标,如果它位于有效成员上,则返回 1(true);如果它位于最后一个成员之外,则返回 0(false)。成员的名称或索引号在第一个输出参数中返回,值在第二个输出参数中返回。例如:

   set test = ["a","b","c"]  // dynamic arrays are zero-based
   set iter = test.%GetIterator()
   while iter.%GetNext(.key, .value) { write "element:"_key_"=/"_value_"/  "}

element:0=/a/  element:1=/b/  element:2=/c/
  • 迭代器光标是单向的,即仅向一个方向移动;它不能返回到以前的成员或以相反的顺序迭代数组。

  • 迭代spaAry稀疏数组时,迭代器将跳过没有赋值的元素。循环访问对象时,属性不一定按可预测的顺序返回。以下示例演示了数组迭代和对象迭代之间的这些差异。

1.1 迭代数组

此示例创建一个稀疏数组。该数组从零开始,具有六个元素,但只有元素 0、1 和 5 具有赋值。JSON 字符串中显示的 null 元素只是未赋值的占位符

   set dynArray=["abc",999]
   set dynArray."5" = "final"
   write dynArray.%Size()_" elements: "_dynArray.%ToJSON()

6 elements: ["abc",999,null,null,null,"final"]
  • %GetNext() 将仅返回具有值的三个元素,并跳过所有未赋值的元素:
   set iterator=dynArray.%GetIterator()
   while iterator.%GetNext(.key,.val) { write !, "Element index: "_key_", value: "_val }

Element index: 0, value: abc
Element index: 1, value: 999
Element index: 5, value: final

1.2 迭代对象

对象属性没有固定的顺序,这意味着可以按任何顺序创建和销毁属性,而无需创建未赋值的值,但更改对象也可能更改 %GetNext()返回属性的顺序。下面的示例创建一个具有三个属性的对象,调用 %Remove() 销毁一个属性,然后添加另一个属性:

   set dynObject={"propA":"abc","PropB":"byebye","propC":999}
   do dynObject.%Remove("PropB")
   set dynObject.propD = "final"
   write dynObject.%Size()_" properties: "_dynObject.%ToJSON()

3 properties: {"propA":"abc","propD":"final","propC":999}

当我们循环访问对象时,%GetNext() 不会按项目的创建顺序返回项目:

   set iterator=dynObject.%GetIterator()
   while iterator.%GetNext(.key,.val) { write !, "Property name: """_key_""", value: "_val }

Property name: "propA", value: abc
Property name: "propD", value: final
Property name: "propC", value: 999

2. 了解稀疏数组和未赋值

动态数组可以是SpaAry,这意味着并非所有数组元素都包含值。例如,您可以为动态数组的元素 100 赋值,即使该数组尚未包含元素 0 到 99。内存中的空间仅为元素 100 处的值分配。元素 0 到 99 是未赋值的,这意味着 0 到 99 是有效的元素标识符,但不指向内存中的任何值。%Size() 方法将返回数组大小 101,但 %GetNext() 方法将跳过未赋值的元素,仅返回元素 100 中的值。

下面的示例通过将新值分配给元素 8 和 11 来创建稀疏数组:

   set array = ["val_0",true,1,"",null,"val_5"] // values 0 through 5
   do array.%Set(8,"val_8")                     // undefined values 6 and 7 will be null
   set array."11" = "val_11"                    // undefined values 9 and 10 will be null
   write array.%ToJSON()

["val_0",true,1,"",null,"val_5",null,null,"val_8",null,null,"val_11"]

尚未将任何值分配给元素 6、7、9 和 10,并且它们在内存中不占用任何空间,但它们在 JSON 字符串中由 null 值表示,因为 JSON 不支持未定义的值。

3. 在稀疏数组中使用 %Remove

%Remove() 方法将未赋值的元素视为与任何其他元素一样。可以有一个数组,该数组仅由未赋值组成。下面的示例创建一个稀疏数组,然后删除未赋值的元素 0。然后,它删除元素 7,该元素现在是唯一包含值的元素:

   set array = []
   do array.%Set(8,"val_8")
   do array.%Remove(0)
   do array.%Remove(7)
   write "Array size = "_array.%Size()_":",!,array.%ToJSON()

Array size = 7:
[null,null,null,null,null,null,null]

3.1 null 和未赋值之间的区别

动态实体包含允许它们区分 null 值和未赋值的元数据。JSON 不指定单独的未定义数据类型,因此在将动态实体序列化为 JSON 字符串时,没有规范的方法来保留这种区别。如果不希望序列化数据中出现额外的 null 值,则必须在序列化之前删除未分配的元素(请“使用 %IsDefined()测试有效值”),或使用一些依赖于应用程序的方法将区别记录为元数据。

4. 具有 %Size() 的稀疏数组迭代

%Size() 方法返回动态实体中的属性或元素数量。例如:

   set dynObject={"prop1":123,"prop2":[7,8,9],"prop3":{"a":1,"b":2}}
   write "Number of properties: "_dynObject.%Size()

Number of properties: 3

spaAry中,此数字包括具有未赋值的元素,如下面的示例所示。在此示例中创建的数组有六个元素,但只有元素 0、1 和 5 具有赋值。JSON 字符串中显示的 null 元素只是未赋值的占位符:

   set test=["abc",999]
   set test."5" = "final"
   write test.%Size()_" elements: "_test.%ToJSON()

6 elements: ["abc",999,null,null,null,"final"]

元素 2、3 和 4 没有赋值,但仍被视为有效的数组元素。动态数组从零开始,因此最后一个元素的索引号将始终为 %Size()-1。下面的示例以相反的顺序循环访问数组测试的所有六个元素,并使用 %Get() 返回它们的值:

   for i=(test.%Size()-1):-1:0 {write "element "_i_" = /"_test.%Get(i)_"/",!}

element 5 = /final/
element 4 = //
element 3 = //
element 2 = //
element 1 = /999/
element 0 = /abc/

对于大于 %Size()-1 的数字,%Get() 方法将返回 “”(空字符串),并对负数引发异常。

5. 使用 %IsDefined() 测试有效值

%IsDefined() 方法测试指定属性名称或数组索引号处是否存在值。如果指定的成员具有值,则该方法返回 1(true);如果该成员不存在,则该方法返回 0(假)。对于稀疏数组中未赋值的元素,它还将返回 false。

如果使用 for 循环循环循环访问稀疏数组,则会遇到未赋值。下面的示例创建一个数组,其中前三个元素是 JSON null、空字符串和未赋值。for 循环被特意设置为超过数组的末尾,并测试数组索引为 4 的元素:

   set dynarray = [null,""]
   set dynarray."3" = "final"
   write dynarray.%ToJSON()
[null,"",null,"final"]

   for index = 0:1:4 {write !,"Element "_index_": "_(dynarray.%IsDefined(index))}

Element 0: 1
Element 1: 1
Element 2: 0
Element 3: 1
Element 4: 0

%IsDefined() 在两种情况下返回 0:元素 2 没有赋值,以及元素 4 不存在

ObjectScript 为 JSON 空值(如本例中的元素 0)返回 “”(空字符串)。如果需要测试 “” 和 null 以及未赋值,请使用 %GetTypeOf() 而不是 %IsDefined()

5.1 测试对象属性是否存在

%IsDefined() 方法还可用于测试对象属性是否存在。下面的代码创建具有三个字符串值的动态数组名称,然后使用前两个字符串创建具有属性 prop1 和 prop2 的对象 dynobj。

   set names = ["prop1","prop2","noprop"]
   set dynobj={}.%Set(names."0",123).%Set(names."1",456)
   write dynobj.%ToJSON()

{"prop1":123,"prop2":456}

下面的代码使用 %IsDefined() 来确定哪些字符串已用作 dynobj 中的属性名称:

   for name = 0:1:2 {write !,"Property "_names.%Get(name)_": "_(dynobj.%IsDefined(names.%Get(name)))}

Property prop1: 1
Property prop2: 1
Property noprop: 0

6. %Push 和 %Pop 在dynAry 中应用

6.1 实现堆栈功能

  • %Push()%Pop() 可用于堆栈操作
  • %Push()%Pop() 方法仅适用于dynAry。它们的工作方式与 %Set()%Remove() 完全相同,只是它们始终添加或删除数组的最后一个元素
   set array = []
   do array.%Set(array.%Size(), 123).%Set(array.%Size(), 456)
   write "removed "_array.%Remove(array.%Size()-1)_", leaving "_array.%ToJSON()

removed 456, leaving [123]

   set array = []
   do array.%Push(123).%Push(456)
   write "removed "_array.%Pop()_", leaving "_array.%ToJSON()

removed 456, leaving [123]

6.2 实现队列功能

您可以通过用 %Remove(0) 代替 %Pop() 来实现队列。

6.3 %Push() 和 %Pop() 构建数组

使用 %Push() 生成一个数组,然后使用 %Pop() 会按相反的顺序删除每个元素。

6.3.1 指定数据类型

  • 利用 .%Push(0,"boolean") 第二个参数,可指定数据类型
  • 下面的例子生成包含嵌套数组的数组。对 %Push() 的最终调用指定可选类型参数,以将布尔值存储为 JSON false 而不是 ObjectScript 0
   set array=[]
   do array.%Push(42).%Push("abc").%Push([])
   do array."2".%Push("X").%Push(0,"boolean")
   write array.%ToJSON()

[42,"abc",["X",false]]

6.3.2 删除布尔值返回0/1

删除嵌套数组的所有元素。像所有动态实体方法一样,%Pop()将返回 ObjectScript 0 而不是 JSON false

   for i=0:1:1 {write "/"_array."2".%Pop()_"/ "}
/0/ /X/

   write array.%ToJSON()
[42,"abc",[]]

6.3.3 删除整个数组

现在删除主数组的所有元素,包括空嵌套数组:

   for i=0:1:2 {write "/"_array.%Pop()_"/ "}
/2@%Library.DynamicArray/ /abc/ /42/

   write array.%ToJSON()
[]
 类似资料: