美文网首页
第三章 Caché JSON 迭代数组

第三章 Caché JSON 迭代数组

作者: Cache技术分享 | 来源:发表于2020-12-30 09:14 被阅读0次

第三章 Caché JSON 迭代数组

动态实体使用标准的迭代方法%GetNext(),该方法同时处理对象和数组。还可以通过按顺序寻址每个元素(使用for循环或类似的结构)来遍历一个数组,但这需要了解一些数组的知识,其中的元素不包含值。由于%GetNext()通过跳过这些元素来避免问题,所以它应该是尽可能首选的迭代方法。

使用%GetNext()遍历动态实体

所有动态实体都提供%GetIterator()方法,该方法返回一个%Iterator实例(可以是%Iterator)。包含指向动态对象或数组成员的指针。%Iterator对象提供%GetNext()方法来获取每个成员的键和值。

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

/// d ##class(PHA.OP.MOB.Test).TestIteratingDynamicEntity()
ClassMethod TestIteratingDynamicEntity()
{
    set test = ["a","b","c"]
    set iter = test.%GetIterator()
    while iter.%GetNext(.key, .value) { 
        write "element:"_key_"=/"_value_"/  "
    }
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestIteratingDynamicEntity()
element:0=/a/  element:1=/b/  element:2=/c/

迭代器指针只向一个方向移动;它不能返回到以前的成员或以相反的顺序迭代数组。

当迭代一个数组时,迭代器跳过没有赋值的元素。在遍历对象时,属性不一定以可预测的顺序返回。下面的示例演示了数组迭代和对象迭代之间的这些区别。

遍历数组

本例创建一个数组。该数组有6个元素,但只有元素0、1和5有赋值。JSON字符串中显示的空元素只是未赋值的占位符:

/// d ##class(PHA.OP.MOB.Test).TestIteratingArray()
ClassMethod TestIteratingArray()
{
   set dynArray=["abc",999]
   set dynArray."5" = "final"
   write dynArray.%Size()_" elements: "_dynArray.%ToJSON()
}
DHC-APP> d ##class(PHA.OP.MOB.Test).TestIteratingArray()
6 elements: ["abc",999,null,null,null,"final"]

%GetNext()将只返回三个有值的元素,跳过所有未分配的元素:

/// d ##class(PHA.OP.MOB.Test).TestIteratingArray()
ClassMethod TestIteratingArray()
{
    set dynArray=["abc",999]
    set dynArray."5" = "final"
    write dynArray.%Size()_" elements: "_dynArray.%ToJSON()
    set iterator=dynArray.%GetIterator()
    while iterator.%GetNext(.key,.val) { 
        write !, "Element index: "_key_", value: "_val 
    }
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestIteratingArray()
6 elements: ["abc",999,null,null,null,"final"]
Element index: 0, value: abc
Element index: 1, value: 999
Element index: 5, value: final

遍历对象

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

/// d ##class(PHA.OP.MOB.Test).TestIteratingObject()
ClassMethod TestIteratingObject()
{
    set dynObject={"propA":"abc","PropB":"byebye","propC":999}
    do dynObject.%Remove("PropB")
    set dynObject.propD = "final"
    write dynObject.%Size()_" properties: "_dynObject.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestIteratingObject()
3 properties: {"propA":"abc","propD":"final","propC":999}

当我们遍历对象时,%GetNext()不会按创建的顺序返回项:

/// d ##class(PHA.OP.MOB.Test).TestIteratingObject()
ClassMethod TestIteratingObject()
{
    set dynObject={"propA":"abc","PropB":"byebye","propC":999}
    do dynObject.%Remove("PropB")
    set dynObject.propD = "final"
    write dynObject.%Size()_" properties: "_dynObject.%ToJSON()
    set iterator=dynObject.%GetIterator()
    while iterator.%GetNext(.key,.val) { 
        write !, "Property name: """_key_""", value: "_val 
    }
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestIteratingObject()
3 properties: {"propA":"abc","propD":"final","propC":999}
Property name: "propA", value: abc
Property name: "propD", value: final
Property name: "propC", value: 999

理解数组和未赋值

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

下面的例子通过给元素8和11赋值来创建一个数组:

/// d ##class(PHA.OP.MOB.Test).TestSparseArrays()
ClassMethod TestSparseArrays()
{
   set array = ["val_0",true,1,null,"","val_5"]
   do array.%Set(8,"val_8")
   set array."11" = "val_11"
   write array.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestSparseArrays()
["val_0",true,1,null,"","val_5",null,null,"val_8",null,null,"val_11"]

注意:创建数组时,定义位置如果超过了数组长度,可以指定数组长度,其他位置为null

元素5、7、9和10没有分配任何值,它们在内存中不占空间,但是它们在JSON字符串中由null值表示,因为JSON不支持未定义的值。

在数组中使用%Remove

方法%Remove()像处理任何其他元素一样处理未分配的元素。可以有一个只包含未赋值的数组。下面的示例创建一个数组,然后删除未分配的元素0。然后删除元素7,它现在是唯一一个包含值的元素:

/// d ##class(PHA.OP.MOB.Test).TestSparseArraysRemove()
ClassMethod TestSparseArraysRemove()
{
    set array = []
    do array.%Set(8,"val_8")
    do array.%Remove(0)
    do array.%Remove(6)
    write "Array size = "_array.%Size()_":",!,array.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestSparseArraysRemove()
Array size = 7:
[null,null,null,null,null,null,"val_8"]

注意:JSON不能保留空值和未赋值之间的区别。 动态实体包含元数据,允许它们区分空值和未赋值。JSON没有指定单独的未定义数据类型,因此在将动态实体序列化为JSON字符串时,没有保持这种区别的规范方法。如果不希望序列化数据中有额外的空值,则必须在序列化之前删除未分配的元素,或者使用一些与应用程序相关的方法将这种区别记录为元数据。

使用%Size()的数组迭代

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

/// d ##class(PHA.OP.MOB.Test).TestObjectSize()
ClassMethod TestObjectSize()
{
    set dynObject={"prop1":123,"prop2":[7,8,9],"prop3":{"a":1,"b":2}}
    write "Number of properties: "_dynObject.%Size()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestObjectSize()
Number of properties: 3

在稀疏数组中,这个数字包括未赋值的元素,如下面的示例所示。本例中创建的数组有6个元素,但只有元素0、1和5有赋值。JSON字符串中显示的空元素只是未赋值的占位符:

/// d ##class(PHA.OP.MOB.Test).TestArraySize()
ClassMethod TestArraySize()
{
    set test=["abc",999]
    set test."5" = "final"
    write test.%Size()_" elements: "_test.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestArraySize()
6 elements: ["abc",999,null,null,null,"final"]

元素2、3和4没有赋值,但仍然被视为有效的数组元素。动态数组是从零开始的,因此最后一个元素的索引号总是%Size()-1

下面的例子以相反的顺序遍历数组test的所有6个元素,并使用%Get()返回它们的值:

/// d ##class(PHA.OP.MOB.Test).TestArraySize()
ClassMethod TestArraySize()
{
    set test=["abc",999]
    set test."5" = "final"
    write test.%Size()_" elements: "_test.%ToJSON(),!
    for i=(test.%Size()-1):-1:0 {write "element "_i_" = /"_test.%Get(i)_"/",!}
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestArraySize()
6 elements: ["abc",999,null,null,null,"final"]
element 5 = /final/
element 4 = //
element 3 = //
element 2 = //
element 1 = /999/
element 0 = /abc/

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

注意 :这里显示的迭代技术仅用于特定目的(比如检测数组中未赋值或以相反的顺序遍历数组)。在大多数情况下,应该使用%GetNext(),它跳过未分配的元素,可以用于动态对象和动态数组。

使用%IsDefined()测试有效值

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

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

/// d ##class(PHA.OP.MOB.Test).TestIsDefined()
ClassMethod TestIsDefined()
{
    set dynarray = [null,""]
    set dynarray."3" = "final"
    write dynarray.%ToJSON(),!
    for index = 0:1:4 {
        write !,"Element "_index_": "_(dynarray.%IsDefined(index))
    }
}

DHC-APP>d ##class(PHA.OP.MOB.Test).TestIsDefined()
[null,"",null,"final"]
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().

注意:正如在前一节中提到的,不应该为迭代使用for循环,除非在一些不寻常的情况下。在大多数情况下,应该使用%GetNext()方法,该方法跳过未赋值。

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

/// d ##class(PHA.OP.MOB.Test).TestIsDefinedObject()
ClassMethod TestIsDefinedObject()
{
   set names = ["prop1","prop2","noprop"]
   set dynobj={}.%Set(names."0",123).%Set(names."1",456)
   write dynobj.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestIsDefinedObject()
{"prop1":123,"prop2":456}

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

/// d ##class(PHA.OP.MOB.Test).TestIsDefinedObject()
ClassMethod TestIsDefinedObject()
{
   set names = ["prop1","prop2","noprop"]
   set dynobj={}.%Set(names."0",123).%Set(names."1",456)
   write dynobj.%ToJSON()
   for name = 0:1:2 {write !,"Property "_names.%Get(name)_": "_(dynobj.%IsDefined(names.%Get(name)))}
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestIsDefinedObject()
{"prop1":123,"prop2":456}
Property prop1: 1
Property prop2: 1
Property noprop: 0

注意:把[]中的属性作为{}的属性来赋值

在动态数组中使用%Push%Pop

%Push()%Pop()方法仅对动态数组可用。

它们的工作方式与%Set()%Remove()完全相同,只是它们总是添加或删除数组的最后一个元素。例如,下面的代码使用任意一组方法生成相同的结果。

/// d ##class(PHA.OP.MOB.Test).TestPopPush()
ClassMethod TestPopPush()
{
    set array = []
    do array.%Set(array.%Size(), 123).%Set(array.%Size(), 456)
    write "removed "_array.%Remove(array.%Size()-1)_", leaving "_array.%ToJSON(),!
    
    set array = []
    do array.%Push(123).%Push(456)
    write "removed "_array.%Pop()_", leaving "_array.%ToJSON(),!
}

注意:每次%Set%Size都返回当前数组的长度。

DHC-APP>d ##class(PHA.OP.MOB.Test).TestPopPush()
removed 456, leaving [123]
removed 456, leaving [123]

虽然%Push()%Pop()用于堆栈操作,但是可以通过用%Remove(0)替换%Pop()来实现队列。下面的示例使用%Push()构建一个数组,然后使用%Pop()以相反的顺序删除每个元素。

使用%Push()%Pop()构建一个数组并销毁

构建一个包含嵌套数组的数组。对%Push()的最后一次调用指定了一个可选的类型参数,该参数将一个布尔值存储为JSON 0 false,1 true,而不是Caché 0

/// d ##class(PHA.OP.MOB.Test).TestPush()
ClassMethod TestPush()
{
    set array=[]
    do array.%Push(42).%Push("abc").%Push([])
    do array."2".%Push("X").%Push(0,"boolean")
    write array.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestPush()
[42,"abc",["X",false]]

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

/// d ##class(PHA.OP.MOB.Test).TestPush()
ClassMethod TestPush()
{
    set array=[]
    do array.%Push(42).%Push("abc").%Push([])
    do array."2".%Push("X").%Push(1,"boolean")
    write array.%ToJSON(),!
    for i=0:1:1 {write "/"_array."2".%Pop()_"/ "}
    write array.%ToJSON(),!
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestPush()
[42,"abc",["X",true]]
/1/ /X/ [42,"abc",[]]

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

/// d ##class(PHA.OP.MOB.Test).TestPush()
ClassMethod TestPush()
{
    set array=[]
    do array.%Push(42).%Push("abc").%Push([])
    do array."2".%Push("X").%Push(1,"boolean")
    write array.%ToJSON(),!
    for i=0:1:1 {write "/"_array."2".%Pop()_"/ "}
    write array.%ToJSON(),!
    for i=0:1:2 {write "/"_array.%Pop()_"/ "}
    write array.%ToJSON(),!
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestPush()
[42,"abc",["X",true]]
/1/ /X/ [42,"abc",[]]
/2@%Library.DynamicArray/ /abc/ /42/ []
 

为了简单起见,这些示例使用硬编码的for循环。

相关文章

网友评论

      本文标题:第三章 Caché JSON 迭代数组

      本文链接:https://www.haomeiwen.com/subject/udvcoktx.html