第二章 Caché JSON 创建和修改动态实体
第二章 Caché JSON 创建和修改动态实体
使用JSON文字构造器
动态实体是%DynamicObject
或%DynamicArray
的实例,它们的设计目的是将JSON数据操作无缝地集成到Caché中。虽然可以使用标准的%New()
方法创建这些类的实例,但是动态实体支持一组更加灵活和直观的构造函数。JSON文字构造函数允许通过直接将JSON字符串赋值给变量来创建动态实体。例如,下面的代码创建了%DynamicObject
和%DynamicArray
的空实例:
set dynamicObject = {}
set dynamicArray = []
write dynamicObject,!,dynamicArray
3@%Library.DynamicObject
1@%Library.DynamicArray
与%New()
构造函数不同,文字构造函数{}
和[]
可以接受JSON格式的字符串作为参数。例如,下面的代码创建了一个名为prop1
的动态对象:
set dynamicObject = {"prop1":"a string value"}
write dynamicObject.prop1
a string value
事实上,JSON文字构造函数{}
和[]
可用于指定任何有效的JSON数组或对象结构。简单地说,任何有效的JSON文字字符串也是计算为动态实体的有效Caché表达式。
注意:JSON属性名必须始终带引号。JSON语言规范是Javascript对象表示法的子集,并在某些领域实施更严格的规则。一个重要的区别是JSON规范要求所有属性名都用双引号括起来。另一方面,Javascript语法在很多情况下允许不带引号的名称。
动态实体在JSON字符串中存储每个对象属性或数组元素的精确表示。任何动态实体都可以使%ToJSON()
方法将存储的数据作为JSON字符串返回。在转换为字符串或从字符串转换时,不会丢失或损坏数据。下面的示例创建了一个动态数组,然后调用%ToJSON()
构造并返回一个表示存储数据的新JSON字符串:
set dynamicArray = [[1,2,3],{"A":33,"a":"lower case"},1.23456789012345678901234,true,false,null,0,1,""]
write dynamicArray.%ToJSON()
[[1,2,3],{"A":33,"a":"lower case"},1.23456789012345678901234,true,false,null,0,1,""]
动态数组已经存储和返回了几个重要的值:
- 前两个元素是一个嵌套数组和一个嵌套对象。在JSON语法中,数组和对象结构可以嵌套到任意深度。
- 属性名区分大小写。嵌套对象有两个不同的属性,分别称为“A”和“a”。
- 第三个值是一个非常高精度的小数。如果将该值存储为标准浮点数,则该值将被舍入,但是动态数组保留了原始值的精确表示。
- 最后6个元素包含JSON数据类型值
true
、false
和null
,以及对应的Caché值0
,1
和""
。同样,动态实体保留每个值的精确表示。
使用动态表达式和点语法
在JSON中存储值的方式与在ObjectScript中表示值的方式之间存在显著差异。
如果每次需要使用ObjectScript值时都必须将其转换为JSON或从JSON转换,那么JSON数据存储就不是很有用,因此动态实体的设计使转换过程透明。总是可以存储和检索ObjectScript值,而不必担心它在JSON语法中的表示形式.
字面JSON构造函数也不例外。到目前为止,所有的示例都完全是JSON语法,但是文字构造函数也可以接受动态表达式中定义的值,这些值就是用括号括起来的ObjectScript表达式。
例如,下面的动态数组构造函数存储两个Unicode字符。在运行时,文字构造函数计算每个元素并存储计算后的值。第一个元素是用JSON语法定义的,第二个元素是Caché函数调用,但是结果存储值是相同的:
ClassMethod TestDynamicExpressions()
{
write ["\u00E9",($CHAR(233))].%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestDynamicExpressions()
["é","é"]
可以将ObjectScript表达式看作是set
语句右侧的代码。任何计算为值而不是对象引用的ObjectScript表达式都可以序列化为JSON文字字符串。下面的示例在对象属性obj.l
中存储一个$LIST
值(它是带分隔符的字符串,而不是对象).然后创建数组并提取obj
中的每个列表项。列表到一个单独的元素:
ClassMethod TestDynamicExpressions()
{
write ["\u00E9",($CHAR(233))].%ToJSON()
set obj = {"list":($LISTFROMSTRING("Deborah Noah Martha Bowie"," "))}
set array = [($LIST(obj.list,1)),($LIST(obj.list,2)),($LIST(obj.list,3)),($LIST(obj.list,4))]
write obj.%ToJSON(),!,array.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestDynamicExpressions()
["é","é"]
{"list":"\t\u0001Deborah\u0006\u0001Noah\b\u0001Martha\u0007\u0001Bowie"}
["Deborah","Noah","Martha","Bowie"]
不能使用动态表达式来定义属性名(尽管有通过编程方式定义属性名的方法。当然,文字构造函数并不是操作对象属性和数组元素的唯一方法。例如,下面的代码创建了一个空的动态对象,并使用标准的点对象 语法来定义内容:
ClassMethod TestDynamicExpressions()
{
set dynArray = []
set dynArray."0" = "02"_"33"
set dynArray."1" = {}
set dynArray."1".foo = $CHAR(dynArray."0")
write dynArray.%ToJSON(),!
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestDynamicExpressions()
["0233",{"foo":"é"}]
DHC-APP>
在本例中,文字构造函数仅用于创建空的动态实体。赋值语句遵循一些简单的规则:
- 所分配的值是标准的Caché表达式。(在本例中,
“02”_“33”
是一个计算结果为整数233的Caché字符串表达式)。 - 数组元素由数组索引号地址,索引号必须是双引号括起的数字文字。
- 对象属性由属性名地址。虽然属性名是字符串文字,但如果属性名是有效的Caché类成员名,则双引号是可选的。
- 如果指定的实体成员尚不存在,则在为其赋值时将创建它。
如前所述,值总是以ObjectScript格式存储和检索,而不管它们在JSON语法中是如何表示的。下面的示例演示了在使用点语法时应该注意的更多事实。
使用点语法创建动态对象属性
本例使用一个文本构造函数和点语法来创建动态对象dynObj,其中包含名为A
、a
和C
的属性quote
。在文字字符串中,所有属性名都必须加引号。在set
语句和write
语句中,属性名A
或a
不需要引号,但C
必须使用引号:
set dynObj = {"a":"stuff"}
set dynObj."C quote" = " ""C quote"" contains a space "
set dynObj.a = " lower case ""a"" "
set dynObj.A = " upper case ""A"" "
write !,dynObj.%ToJSON()
{"a":" lower case \"a\" ","C quote":" \"C quote\" contains a space ","A":" upper case \"A\" "}
动态对象是无序列表,所以值不一定按创建它们的顺序存储。
使用点语法创建动态数组元素
本例在定义元素2之前将一个值赋给数组元素3。元素不必按顺序定义,而元素2可能没有定义。
set dynArray = [true,false]
set dynArray."3" = "three"
set dynArray."2" = 0
write dynArray.%ToJSON()
[true,false,0,"three"]
尽管前两个元素被定义并存储为JSON boolean值true
和false
,但它们以整数1和0的形式返回,这两个值是等价的Caché boolean值:
write "0=/"_dynArray."0"_"/, 1=/"_dynArray."1"_"/, 2=/"_dynArray."2"_"/, 3=/"_dynArray."3"_"/"
0=/1/, 1=/0/, 2=/0/, 3=/three/
由于存储值总是以ObjectScript格式返回,因此JSON true
、false
和null
将作为Caché 0
、1
和“”
(空字符串)返回。但是,原始JSON值保留在动态实体中,必要时可以恢复。
注意:点语法不应该与很长的属性名一起使用
虽然动态对象属性可以有任意长度的名称,但是ObjectScript不能使用超过180个字符的属性名称。如果动态对象属性名超过此限制,则尝试在点语法中使用该名称将导致Caché抛出具有误导性<PROPERTY DOES NOT EXIST>
错误,即使该属性存在且名称有效。可以使用%Set()
和%Get()
方法来避免这个错误,这两个方法接受任意长度的属性名。
使用 %Set(), %Get(), %Remove()
虽然文字构造函数和点语法可用于创建动态实体成员和操作值,但它们并不适合所有目的。动态实体为创建、读取、更新和删除操作提供了%Set()
、%Get()
和%Remove()
方法。
这些方法最重要的优点之一是成员标识符(属性名和数组索引号)不必是文字。可以使用ObjectScript变量和表达式来指定值和标识符。
使用%Set()、%Get()和%Remove()以编程方式指定值和标识符
下面的示例使用文字构造函数{}
创建一个对象,并调用新对象的%Set()
方法来添加一系列名为propn
的属性,其值为100+n
。名称和值都由ObjectScript表达式定义:
ClassMethod TestSetGetRemove()
{
set dynObj = {}
for i=1:1:5 { do dynObj.%Set("prop"_i,100+i) }
write dynObj.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestSetGetRemove()
{"prop1":101,"prop2":102,"prop3":103,"prop4":104,"prop5":105}
注意:%Set可以自定义属性
可以使用与%Get()
相同的变量来检索属性值:
ClassMethod TestSetGetRemove()
{
set dynObj = {}
for i=1:1:5 { do dynObj.%Set("prop"_i,100+i) }
write dynObj.%ToJSON(),!
for i=1:1:5 { write dynObj.%Get("prop"_i)_" " }
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestSetGetRemove()
{"prop1":101,"prop2":102,"prop3":103,"prop4":104,"prop5":105}
101 102 103 104 105
方法的作用是:从动态实体中删除指定的成员并返回值。这个示例删除了5个属性中的3个,并将返回值连接到字符串removedValues
。write
语句显示删除的值的字符串和dynObj
的当前内容:
ClassMethod TestSetGetRemove()
{
set dynObj = {}
for i=1:1:5 { do dynObj.%Set("prop"_i,100+i) }
write dynObj.%ToJSON(),!
for i=1:1:5 { write dynObj.%Get("prop"_i)_" ",! }
set removedValues = ""
for i=2:1:4 { set removedValues = removedValues_dynObj.%Remove("prop"_i)_" " }
write "Removed values: "_removedValues,!,"Remaining properties: "_dynObj.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestSetGetRemove()
{"prop1":101,"prop2":102,"prop3":103,"prop4":104,"prop5":105}
101
102
103
104
105
Removed values: 102 103 104
Remaining properties: {"prop1":101,"prop5":105}
虽然在这些简单的示例中使用了for
循环,但是常规的迭代方法是%GetNext()
%Get()``和%Remove()
都返回指定成员的Caché ObjectScript值,但是在如何返回嵌入的动态实体方面有一个重要的区别:
-
%Get()
通过引用返回值。返回值是属性或元素的OREF(对象引用),而属性或元素又包含对嵌入实体的引用。 -
%Remove()
销毁指定的属性或元素(使成员OREF无效),但返回一个有效的OREF
,该OREF
直接指向以前嵌入的实体。
使用%Get()和%Remove()检索嵌套的动态实体
在下面的示例中,属性dynObj
的值。address
是一个动态对象。%Get()
语句在变量addrPointer
中存储对属性(而不是属性值)的引用。此时,addrPointer
可以用来访问嵌入address
实体的road
属性:
ClassMethod TestNestedDynamic()
{
set dynObj = {"name":"greg", "address":{"road":"Old Road"}}
set addrPointer = dynObj.%Get("address")
set dynObj.address.road = "New Road"
write "Value of "_addrPointer_" is "_addrPointer.road
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestNestedDynamic()
Value of 2@%Library.DynamicObject is New Road
%Remove()
语句将销毁该属性并向属性值返回一个新的OREF
。
ClassMethod TestNestedDynamic()
{
set dynObj = {"name":"greg", "address":{"road":"Old Road"}}
set addrPointer = dynObj.%Get("address")
set dynObj.address.road = "New Road"
write "Value of "_addrPointer_" is "_addrPointer.road,!
set addrRemoved = dynObj.%Remove("address")
write "OREF of removed property: "_addrPointer,!,"OREF returned by %Remove(): "_addrRemoved,!
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestNestedDynamic()
Value of 2@%Library.DynamicObject is New Road
OREF of removed property: 2@%Library.DynamicObject
OREF returned by %Remove(): 2@%Library.DynamicObject
可以使用%Remove()
方法按任何顺序删除成员。这对对象和数组有不同的含义,如下面的示例所示。
删除对象属性
对象属性没有固定的顺序。这意味着可以按任意顺序销毁属性,但是删除一个属性并添加另一个属性也可能改变属性序列化和返回的顺序。下面的示例创建了一个动态对象,并使用对%Set()
的三个连续调用定义了三个属性:
/// d ##class(PHA.OP.MOB.Test).TestRemovingProperty()
ClassMethod TestRemovingProperty()
{
set dynObject={}.%Set("propA","abc").%Set("PropB","byebye").%Set("propC",999)
write dynObject.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestRemovingProperty()
{"propA":"abc","PropB":"byebye","propC":999}
现在调用%Remove()
销毁属性PropB
,然后添加新属性PropD
。生成的动态对象不按其创建的顺序序列化其属性:
/// d ##class(PHA.OP.MOB.Test).TestRemovingProperty()
ClassMethod TestRemovingProperty()
{
set dynObject={}.%Set("propA","abc").%Set("PropB","byebye").%Set("propC",999)
write dynObject.%ToJSON(),!
do dynObject.%Remove("PropB")
set dynObject.propD = "added last"
write dynObject.%ToJSON(),!
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestRemovingProperty()
{"propA":"abc","PropB":"byebye","propC":999}
{"propA":"abc","propD":"added last","propC":999}
删除数组元素
数组是有序列表。当对一个元素调用%Remove()
时,该元素之后的所有元素的数组索引号都将减少1。下面的例子连续三次调用%Remove(1)
,每次删除一个不同的元素:
ClassMethod TestRemovingArray()
{
set dynArray = ["a","b","c","d","e"]
set removedValues = ""
for i=1:1:3 { set removedValues = removedValues _ dynArray.%Remove(1) _ " " }
write "Removed values: "_removedValues,!,"Array size="_dynArray.%Size()_": "_dynArray.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestRemovingArray()
Removed values: b c d
Array size=2: ["a","e"]
堆栈操作通常使用%Push()
和%Pop()
来实现,而不是使用%Set()
和%Remove()
,但是可以使用%Remove(0)
来替换%Pop()
来实现队列.%Remove()
以相同的方式处理所有数组,包括那些包含未定义值的元素的数组。
方法链
方法%Set()
和%Push()
返回对它们修改过的实体的引用。返回的引用可以立即用于在相同的表达式中调用同一实体上的另一个方法。
开始链的动态实体可以是构造函数({}
,或[]
)或现有实体。方法%Set()
和%Push()
返回可链引用,可以从链中的任何地方调用。链中的最后一项可以是实体可用的任何方法。
在下面的示例中,单个write
语句使用对%FromJSON()
、%Set()
、%Push()
和%ToJSON()
的链接调用来创建、修改和显示动态数组:
/// d ##class(PHA.OP.MOB.Test).TestMethodChaining()
ClassMethod TestMethodChaining()
{
set jstring = "[123]"
write [].%FromJSON(jstring).%Set(1,"one").%Push("two").%Push("three").%Set(1,"final value").%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestMethodChaining()
[123,"final value","two","three"]
%FromJSON()
只在链中的第一个方法调用时有用,因为它不返回调用实体的修改版本。相反,它只是忽略调用实体并返回一个从JSON字符串反序列化的全新实例。
还可以通过使用%Get()
、%Pop()
、%GetNext()
或%Remove()
检索嵌套实体来启动链。
错误处理
动态实体在出现错误时抛出异常,而不是返回%Status
值。在下面的例子中,抛出的异常包含了足够的信息,可以断定方法参数中的第二个字符是无效的:
/// d ##class(PHA.OP.MOB.Test).TestErrorHandling()
ClassMethod TestErrorHandling()
{
set invalidObject = {}.%FromJSON("{:}")
}
<THROW>%FromJSON+37^%Library.DynamicAbstractObject.1 *%Exception.General Parsing error 3 Line 1 Offset 2
在处理动态数据时,设置一些错误数据。任何使用动态对象的代码都应该在某种程度上被TRY-CATCH
块包围。例如:
/// d ##class(PHA.OP.MOB.Test).TestErrorHandlingTry()
ClassMethod TestErrorHandlingTry()
{
TRY {
set invalidObject = {}.%FromJSON("{:}")
}
CATCH errobj {
write errobj.Name_", "_errobj.Location_", error code "_errobj.Code,!
RETURN
}
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestErrorHandlingTry()
Parsing error, Line 1 Offset 2, error code 3
将动态实体转换JSON
可以使用%ToJSON()
方法来序列化动态实体(将其转换为JSON字符串),使用%FromJSON()
方法来反序列化(将JSON转换为动态实体)。
将动态实体序列化为JSON
下面的示例创建并修改一个动态对象,然后使用%ToJSON()
序列化它并显示结果字符串:
/// d ##class(PHA.OP.MOB.Test).TestToJSON()
ClassMethod TestToJSON()
{
set dynObject={"prop1":true}.%Set("prop2",123).%Set("prop3","foo")
set objString = dynObject.%ToJSON()
write objString
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestToJSON()
{"prop1":true,"prop2":123,"prop3":"foo"}
动态数组序列化的方式是一样的:
/// d ##class(PHA.OP.MOB.Test).TestToJSONArray()
ClassMethod TestToJSONArray()
{
set dynArray=[].%Push("1st value").%Push("2nd value").%Push("3rd value")
set arrayString = dynArray.%ToJSON()
write arrayString
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestToJSONArray()
["1st value","2nd value","3rd value"]
这两个例子都使用了方法链
将JSON反序列化为动态对象
方法的作用是:将JSON字符串转换为动态实体。下面的示例构造一个动态数组并将其序列化为字符串json string。调用%FromJSON()
将jstring反序列化为一个名为newArray
的新动态实体,然后对其进行修改和显示:
/// d ##class(PHA.OP.MOB.Test).TestFromJSONArray()
ClassMethod TestFromJSONArray()
{
set jstring=["1st value","2nd value","3rd value"].%ToJSON()
set newArray={}.%FromJSON(jstring)
do newArray.%Push("new value")
write "New entity:"_newArray.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestFromJSONArray()
New entity:["1st value","2nd value","3rd value","new value"]
注意,这个示例从一个动态对象构造函数({}
)调用%FromJSON()
,即使返回的值是一个动态数组。%FromJSON()
是%DynamicAbstractObject
的一个类方法,因此可以从任何动态实体或构造函数中调用。
克隆%ToJSON()和%FromJSON()
由于对%FromJSON()
的每次调用都会创建一个新的动态实体,因此可以使用它来复制现有实体或初始化一组相同的实体。
在下面的示例中,属性dynObj.address
是一个动态对象。属性由变量addrPointer
引用,通过调用%FromJSON()
克隆属性值,以创建新的动态对象addrClone
:
/// d ##class(PHA.OP.MOB.Test).TestCloneToJSON()
ClassMethod TestCloneToJSON()
{
set dynObj = {}.%FromJSON({"name":"greg", "address":{"road":"Dexter Ave."}}.%ToJSON())
set addrPointer = dynObj.address
set addrClone = {}.%FromJSON(dynObj.address.%ToJSON())
set addrPointer.road = "Wright Ave."
set addrClone.road = "Sinister Ave."
write "Property = "_dynObj.address.%ToJSON(),!,"Clone = "_addrClone.%ToJSON()
}
变量addrPointer
只是对属性dynObj.address
的引用。但addrClone
是一个独立的实例%DynamicObject
,可以修改而不影响原始值:
DHC-APP>d ##class(PHA.OP.MOB.Test).TestCloneToJSON()
Property = {"road":"Wright Ave."}
Clone = {"road":"Sinister Ave."}
将大型动态实体序列化为流
如果动态实体足够大,%ToJSON()
的输出可能超过Caché 字符串的最大可能长度.
本节中的示例使用名为longStr
的最大长度Caché 字符串。下面的代码片段演示了如何生成longStr
:
/// d ##class(PHA.OP.MOB.Test).TestMaxLength()
ClassMethod TestMaxLength()
{
set longStr=""
for i=1:1:$SYSTEM.SYS.MaxLocalLength() { set longStr = longStr_"x" }
write "Maximum string length = "_$LENGTH(longStr)
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestMaxLength()
Maximum string length = 3641144
注意:Caché 字符串最大长度为3641144. 输出字符串没有长度限制
每当表达式使用%ToJSON()
的返回值时,Caché就在程序堆栈上构建字符串,而程序堆栈受长字符串限制。例如,read/write
语句(如write dyn.%ToJSON()
)或赋值语句(如set x=dyn.%ToJSON()
)将尝试将字符串放到堆栈上。以下示例将两个longStr
副本添加到动态数组中,并尝试将序列化后的字符串分配给一个变量,导致Caché返回错误:
/// d ##class(PHA.OP.MOB.Test).TestMaxLength()
ClassMethod TestMaxLength()
{
set longStr=""
for i=1:1:$SYSTEM.SYS.MaxLocalLength() { set longStr = longStr_"x" }
write "Maximum string length = "_$LENGTH(longStr)
set longArray = [(longStr),(longStr)]
set tooBig = longArray.%ToJSON()
}
DHC-APP>d ##class(PHA.OP.MOB.Test).TestMaxLength()
Maximum string length = 3641144
set tooBig = longArray.%ToJSON() }
^
<MAXSTRING>zTestMaxLength+5^PHA.OP.MOB.Test.1
这个问题的一般解决方案是在DO
命令中通过引用传递%ToJSON()
输出,而不实际检查返回值。输出直接写入当前设备,输出长度没有限制。在下面的例子中,设备是一个流。
写入文件流
本例将动态对象longObject
写入文件,然后检索它。变量longStr
是本节开头定义的值:
/// info: yx
/// desc: 解析Json超长字符串,通过存储本地方式
/// d ##class(Demo.JsonDemo).HandleLongStrFile()
ClassMethod HandleLongStrFile()
{
/* 1.定义最大长度字符串 */
s longStr = ""
for i = 1 : 1 : $SYSTEM.SYS.MaxLocalLength() {
s longStr = longStr_"x"
}
w "最大字符串长度 = "_$l(longStr),!
/* 2.创建超长字符串Json对象 */
s longObject = {"a":(longStr), "b":(longStr), "c":"姚鑫"}
/* 3.把超长JSON写如本地文件longObjectFile.txt */
s file=##class(%File).%New("d:\longObjectFile.txt")
d file.Open("WSN")
d longObject.%ToJSON(file)
d file.Close()
/* 5.打开本地txt依次读取字段,只能通过{}.%FromJSON(file)文件方式读取 */
d file.Open("RS")
b //1
w {}.%FromJSON(file).a
b //2
w {}.%FromJSON(file).b
b //3
w {}.%FromJSON(file).c
s newObject = {}.%FromJSON(file)
w !,"属性 newObject.a 字符长度为: " _ $l(newObject.a)
d file.Close()
}
注意: 此方法测试无效,谁看到解决可以联系我
此解决方案还可以用于从其他流读取输入。
读取和写入流
在本例中,我们序列化了两个大型动态实体(使用临时流,因为%ToJSON()
每个流只能序列化一个实体)。标准流处理方法用于将每个临时流存储为流bigLines
中的单独行:
/// info: yx
/// desc: 解析JsonObject超长字符串Demo
/// d ##class(Demo.JsonDemo).HandleLongStrObject()
ClassMethod HandleLongStrObject()
{
/* 1.定义最大长度字符串 */
s longStr = ""
for i = 1 : 1 : $SYSTEM.SYS.MaxLocalLength() {
s longStr = longStr_"x"
}
w "最大字符串长度 = "_$l(longStr),!
/* 2.创建临时流对象 */
s tmpObject = ##class(%Stream.GlobalCharacter).%New()
/* 3.创建超长字符串Json对象 */
s dyn = {"d":"美国","a":(longStr),"b":(longStr),"c":(longStr)}
/* 4.通过%ToJSON把超长字符串JSON添加到流对象中 */
d dyn.%ToJSON(tmpObject)
/* 5.创建流对象并复制tmpObject对象 */
s bigLines = ##class(%Stream.GlobalCharacter).%New()
d bigLines.CopyFrom(tmpObject)
d bigLines.Rewind()
/* 6.读取流 */
s count= 0
while ('bigLines.AtEnd) {
s count = count + 1
/* 赋值变量提示超长错误MAXSTRING */
//s obj = {}.%FromJSON(bigLines.ReadLineIntoStream())
//w "c :"_obj.c
w !,{}.%FromJSON(bigLines.ReadLineIntoStream()).d,!
b
/* 7.每次读完属性要重新复位 */
do bigLines.Rewind()
w !,{}.%FromJSON(bigLines.ReadLineIntoStream()).c
w "count"_count,!
}
}
/// info: yx
/// desc: 解析JsonArray超长字符串Demo
/// d ##class(Demo.JsonDemo).HandleLongStrArray()
ClassMethod HandleLongStrArray()
{
s longStr = ""
for i = 1 : 1 : $SYSTEM.SYS.MaxLocalLength() {
s longStr = longStr_"x"
}
w "最大字符串长度 = "_$l(longStr)
s tmpArray = ##class(%Stream.GlobalCharacter).%New()
s dyn = [(longStr),(longStr)]
d dyn.%ToJSON(tmpArray)
s bigLines = ##class(%Stream.GlobalCharacter).%New()
d bigLines.CopyFrom(tmpArray)
d bigLines.Rewind()
s count= 0
while ('bigLines.AtEnd) {
s count = count + 1
w !,{}.%FromJSON(bigLines.ReadLineIntoStream())."0"
b
/* 每次读完属性要重新复位 */
do bigLines.Rewind()
w !,{}.%FromJSON(bigLines.ReadLineIntoStream())."1"
w "count"_count,!
}
}
稍后,我们可以从bigLines
反序列化每个动态实体:
DHC-APP>d ##class(PHA.OP.MOB.Test).TestCharacterStreams()
Maximum string length = 3641144
6@%Library.DynamicArray
6@%Library.DynamicObject