写在前面
为了与大家保持一个愉快的沟通,以及便于描述方便,本文做了一些术语简写,如下:
尽量不讲废话
以总结的方式编写
dynEnt
:动态实体dynObj
:动态对象dynAry
:动态数组textConstructor
( 文本构造器):文字 JSON 构造函数{}
和[]
jsonStr
:文本 JSON 字符串
*
:表示重点掌握-
:废话可忽略spaAry
(稀疏数组):有些元素没有值1. 使用 %GetNext()迭代动态实体
动态实体使用标准迭代方法 %GetNext()
,该方法同时适用于对象和数组。还可以通过按顺序对每个元素进行寻址(使用 for
循环或类似结构)来循环访问数组,但这可能需要一些稀疏数组spaAry
的知识,这些数组具有不包含值的元素。由于 %GetNext()
通过跳过这些元素来避免问题,因此它应该尽可能成为首选的迭代方法。
所有动态实体都提供 %GetIterator()
方法,该方法返回一个 %Iterator
实例(%Iterator.Object
或% Iterator.Array
),其中包含指向dynObj
或dynAry
成员的指针。%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
稀疏数组时,迭代器将跳过没有赋值的元素。循环访问对象时,属性不一定按可预测的顺序返回。以下示例演示了数组迭代和对象迭代之间的这些差异。
此示例创建一个稀疏数组
。该数组从零开始,具有六个元素,但只有元素 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
对象属性没有固定的顺序,这意味着可以按任何顺序创建和销毁属性,而无需创建未赋值的值,但更改对象也可能更改 %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]
动态实体包含允许它们区分 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()
%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 中应用
%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]
您可以通过用 %Remove(0)
代替 %Pop()
来实现队列。
使用 %Push()
生成一个数组,然后使用 %Pop()
会按相反的顺序删除每个元素。
.%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]]
删除嵌套数组的所有元素。像所有动态实体方法一样,%Pop()
将返回 ObjectScript 0
而不是 JSON false
:
for i=0:1:1 {write "/"_array."2".%Pop()_"/ "}
/0/ /X/
write array.%ToJSON()
[42,"abc",[]]
现在删除主数组的所有元素,包括空嵌套数组:
for i=0:1:2 {write "/"_array.%Pop()_"/ "}
/2@%Library.DynamicArray/ /abc/ /42/
write array.%ToJSON()
[]