美文网首页
第三章 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