The Good Old Days
The %Library.DynamicObject class has been in IRIS since before it became IRIS. If you have been using it since the Cache days, you may want to brush up on some of its changes.
In Cache 2018, the %Get method only had one argument. It was the key to retrieving from the JSON, meaning that if your JSON object called myObj, it would look like the following:
{
“mybool”:true,
“mynum”:1234,
“mystring”:”Hello World!”
}
Exploiting myObj.%Get(“mybool”) would return a 1, myObj.%Get(“mynum”) would return 1234, and myObj.%Get(“mystring”) would return the string “Hello World!”
Setting those parameters, on the other hand, required a bit more work. For instance, appointing a JSON property to a 0 could mean the number 0, a boolean value meaning false, or a literal string “0”. This is why the %Set method always had the third, optional argument. To create the above-mentioned JSON object, we could utilize the following code:
set myObj = ##class(%Library.DynamicObject).%New()
do myObj.%Set(“mybool”,1,”boolean”)
do myObj.%Set(“mynum”,1234,”number”)
do myObj.%Set(“mystring”,”Hello World!”,”string”)
Yet, in this case, we could also leave the third argument out of the last two. Then, 1234 would be recognized as numeric since it is not quoted, whereas “Hello World!” would be identified as a string because it is quoted. If we wanted to add the value 1234 to the object as a string, we could change the string to numeric. We could also specify the “null” type. In that case, the value must be “”. In practice, however, we often add those values from variables in our ObjectScript code. Due to that, it may be better to specify this argument, just in case the variable might be a string or numeric, to ensure that our JSON arrives at its destination encoded correctly.
How I Learned to Stop Worrying About
As the wise Billy Joel once said, “The good old days weren’t always good, and tomorrow ain’t as bad as it seems.” The type list for %Set has grown, and the %Get method has acquired a couple of new arguments. What is crucial, they both support “stream” as a type. If you have ever handled receiving a lot of JSON data, you have probably seen a
try{
Set mydata = myObj.%Get(“mydata”,”N/A”)
}
catch ex{
if ex.Name = “”{
set mydata = myObj.%Get(“mydata”,,”stream”)
}
}
It will attempt to set mydata to the value located inside “mydata” in the JSON object. If that item does not exist, it will return “N/A” instead. If that item is too long for a string, the system will throw an exception to the catch block. We should check the name of that exception since if a different exception occurred, it would not make sense to try to get the data as a stream either. You can read more on exception handling here. If it is
If you take the approach described above, you will not know whether mydata in the code is a string or a stream. It means that you will have to follow up with the code similar to the one below:
if $ISOBJECT(mydata){
//Place handling for streams here
}
else{
//Place handling for strings here
}
You could also employ the stream option every single time guaranteeing that you always have a stream to work with. However, it would create unnecessary resource usage and overhead in your code.
Another option is to add a stream to a dynamic object using %Set. Check out the following example:
set mystream = ##class(%Stream.FileBinary).%New()
do mystream.LinkToFile(“/path/to/your/file”)
do myObj.%Set(“mydata”,mystream,”stream”)
The data in your file will now go into the mydata field of your dynamic object.
%Set and %Get also encode and decode strings with the help of the Base64 encoding.
Always keep in mind that Base64 encoding is encoding not encryption! There are no secret keys or passphrases to decode your message, and it is easily reversible. Therefore, you should still use an encrypted protocol, e.g., TLS or HTTPS, for transmission! Base64 is operated to transmit non-ASCII characters in a way that allows ASCII-only systems to receive them and pass them along.
With that crucial side note out of the way, we can finally look at how this works. If we make one small change to the previous code sample, the contents of the file stream will become Base64 encoded.
set mystream = ##class(%Stream.FileBinary).%New()
do mystream.LinkToFile(“/path/to/your/file”)
do myObj.%Set(“mydata”,mystream,”stream>base64”)
On the other hand, if the data in the file was already Base64 encoded and we wanted to convert it to the decoded data, we only need to change one character.
set mystream = ##class(%Stream.FileBinary).%New()
do mystream.LinkToFile(“/path/to/your/file”)
do myObj.%Set(“mydata”,mystream,”stream
The greater than or less than signs always point in the direction where conversion takes place. If we convert from an unencoded stream to a Base64 string, the sign will point to the Base64. If we convert a Base64 encoded stream to an unencoded stream, the sign will point from the Base64 to the stream. The same functionality exists for strings when using string>base64 and string
set something = myObj.%Get(“something”,”NA”,”string>base64”)
If the “something” item exists, it will be returned in its Base64 encoded form. However, if it does not exist, “NA” will be returned without being encoded.
There is one caveat to the Base64 encoding option. Only characters with a code between zero and 255 can be encoded in Base64. Character codes greater than 255 will result in a
set mychar = $C(256)
do myobj.%Set(“mychar”,mychar,”string>base64”)
So, I Heard You Like JSON . . .
Sometimes, there is JSON in your JSON. The default way of handling this is usually the one you would choose. Yet, another option has been added to the type argument to deal with a different use case. It is the “json” type. Look at the following JSON object:
{
“mystring”:”Hello World!”,
“mynumber”:1234,
“myjson”:{
“subitem1”:”Hello Mars!”,
“subitem2”:”Hello Stars!”
}
}
As a rule, when you run into this, you will operate the %Get method with the key “myjson” to get a dynamic object. Look at the example below:
set myjson = myobj.%Get(“myjson”)
write myjson.%Get(“subitem1”)
The line above would write “Hello Mars!” This is the most common use case in this situation. Yet, there may be situations when you would prefer to get the actual JSON contained within that item as a string. In that case, we can do the following:
set myjson = myobj.%Get(“myjson”,,”json”)
write myjson
It will write out the JSON string exactly the way it is:
{“subitem1”:”Hello Mars!”,”subitem2”:”Hello Stars!”}
It can come in handy in cases where we want to pass along the JSON as it is to another process. Note that, unlike all the other new types, this one is supported only for the %Get method, not the %Set method.
Hip, Hip, Array!
We have been discussing these new objects in the context of the %Library.DynamicObject class so far, but they are also supported for the %Library.DynamicArray class. In that class, %Set and %Get support the same type arguments as in the %Library.DynamicObject class. The dynamic array class has an additional %Push method though. It supports the same types as %Set, excluding the JSON type.
Without further ado, it is probably the right time to review your older codes and implement these changes to your benefit!