Dynamic Entities (objects and arrays) in IRIS are incredibly useful in situations where you are having to transform JSON data into an Object Model for storage to the database, such as in REST API endpoints hosted within IRIS. This is because these dynamic objects and arrays can easily serve as a point of conversion from one data structure to the other.
Dynamic Objects
Dynamic Objects are very similar to the standard ObjectScript object model you get when you create a new instance of a class object, but with some key differences:
- Dynamic objects have no real type, other than being a DynamicObject or DynamicArray
- They cannot inherit or be morphed directly into a class object
- They're much easier to create and come with a host of built-in functions for managing and manipulating them
To create a dynamic object, it's as easy as:
set obj = {}
We now have "obj" as a dynamic object. If you've used Javascript/Typescript before, this will look very similar. This connection to Javascript becomes very important when talking about arrays later.
With our new dynamic object, we can add and manipulate properties of this object just like we can with class objects, which the added flexibility of not having to predefine properties that the object can use.
set obj.message = "This is a test message"
set obj.num = 4
set obj.test = 1
So far, this all looks similar to the standard ObjectScript model. Here's where things get interesting:
set obj.subobj = {}
Now, we've created a completely new dynamic object within our existing dynamic object, with all the same functionality as the existing one.
set obj.subobj.message = "This is a message on the sub-object."
As you can see, we can string the dot notation together to access properties of sub-objects on a dynamic object. We can also assign dynamic objects as sub-objects on an existing object:
set obj1 = {}
set obj1.message = "This is object 1"
set obj2 = {}
set obj2.message = "This is a different object, object 2"
set obj1.subobj = obj2
set obj.obj1 = obj1
You can also use the %Set(), %Get(), and %Remove() methods on the object (and arrays) to manipulate the data stored in object properties, which is especially useful for properties that have long names. %Remove() is the only real way to remove a property or element from a dynamic object or array.
Dynamic Arrays
We've talked about dynamic objects a lot up to this point, but what about dynamic arrays?
These work very similarly to the objects. To create one:
set array = [1,2,"three",true]
Notice that rather than creating the empty array first and then adding items to it, I've given it a starting value. This can also be done with objects:
set test = {"message":"Test message","num":4}
Also notice that when defining objects this way, the property names must be quoted or else you will get a syntax error.
NOTE: It is CRITICAL to understand that dynamic arrays will be 0-indexed, as opposed to standard IRIS arrays which are 1-indexed. All dynamic arrays start at index 0.
Getting back to arrays, once you have your array you have access to some standard methods for interacting with it, such as:
- %Push(element) - Add a new element to the end of the existing array
- %Pop() - Remove and return the last element in the array
- %Size() - Get the number of elements currently in the array
- %IsDefined(element number) - Returns whether or not the given key (array index number) exists in the array.
Combining Objects and Arrays
As is standard in JSON, you can have an object with properties containing arrays and arrays that contain a set of objects. As shown above when setting sub-objects onto other objects, you can do the same for dynamic arrays as well.
Adding an array to an object:
set obj = {"message":"This is a message"}
set array = [1,2,3,4,5]
set obj.array = array
Adding an object to an array:
set newobj = {"message1":"Message 1", "message2":"Message 2"}
set newarray = [1,2,3]
do newarray.%Push(newobj)
JSON Conversion and Formatting
More than likely, in a real setting these entities are likely to be quite large and difficult to mentally keep track of what properties are where, and what is in an array.
For instance, let's say we have this code:
set testObj = {}
set testObj.message = "This is a test message"
set testObj.num = 4
set subobj = {}
set subobj.message = "This is a message on the sub-object."
set testObj.subobj = subobj
set array = [1,2,3,4,5]
set newobj = {"six":6}
do array.%Push(newobj)
set testObj.array = array
There is a lot going on here with a mix of arrays and objects that make it difficult to mentally map out. To help with this, we can make use of the built in %ToJSON() method which will convert the object into a properly formatted JSON string:
write testObj.%ToJSON()
Which will produce this output:
{"message":"This is a test message","num":4,"subobj":{"message":"This is a message on the sub-object."},"array":[1,2,3,4,5,{"six":6}]}
This is a good start, but it's not very readable. Let's have a look at the %JSON.Formatter class, which has a Format method. To use this, we can create a new instance of %JSON.Formatter, and then call the Format method which takes the JSON string as an input.
set formatter = ##class(%JSON.Formatter).%New()
do formatter.Format(testObj.%ToJSON())
Which gives us a nicely expanded, properly formatted JSON object display:
{
"message":"This is a test message",
"num":4,
"subobj":{
"message":"This is a message on the sub-object."
},
"array":[
1,
2,
3,
4,
5,
{
"six":6
}
]
}
Working with True/False in Dynamic Objects
Standard IRIS does not understand the keywords "true" or "false" typically. To represent these values in IRIS ObjectScript, we must use 1 or 0.
However, when working with dynamic entities, you begin crossing more into Javascript territory which uses "true" and "false" just fine, but there are some limitations.
When defining an object or array, if you're working with a value or set of values contained within the [ ] or { }, then you can set properties or elements as "true" or "false" with no problem. These values will be represented as the Javascript representation of true and false when defined in this way. Outside of that context, IRIS will convert these values to 1 or 0 when displaying them in an ObjectScript context.
If, however, you're using dot syntax to manually set a property on an object, you can only use the IRIS syntax of 1 or 0, otherwise IRIS will throw a syntax error.This has the unfortunate side effect that the Javascript representation sees 1 and 0 as valid values, but with a very different meaning than what may be intended.
To work around this, you can use the %Set(key, default, type) method which takes the key you want to set (object property or array element), an optional default value, and then a string type value, which can be "null", "string", "number", or most importantly, "boolean". When using the %Set() method with a type of "boolean", the 1 and 0 values are then converted to the Javascript representation of "true" and "false". The %Push() method on dynamic arrays can also take a type parameter in the same way. Arrays also have the %Set() method to set a value of a given array index.
See the documentation on %Set() and %Push() for details.
Converting a JSON string into a dynamic object or array
So, this is all great when we want to create a JSON object to perhaps send as a response to a REST API request. Simply setup a dynamic object with all the appropriate properties, and write it out and the downstream system will see it as a proper JSON object.
But, what about that initial request that has a POST body in JSON format? How can that be converted from the JSON string into a dynamic object or array?
Similar to the %ToJSON() method we used earlier, there is also a %FromJSON() method which takes the JSON string as the parameter and sets up the object/array according to the JSON definition.
For example, if we use the same JSON string we got earlier:
set jsonString = {"message":"This is a test message","num":4,"subobj":{"message":"This is a message on the sub-object."},"array":[1,2,3,4,5,{"six":6}]}
We can convert this into an object like this:
set obj = {}.%FromJSON(jsonString)
This will set the "obj" variable to be the dynamic object representation of the given JSON.
Now, let's say we have an array of books that we need to process:
[
{
"id": 1,
"title": "ObjectScript for Dummies",
"type": "paperback"
},
{
"id": 2,
"title": "Intermediate ObjectScript",
"type": "paperback",
},
{
"id": 3,
"title": "Advanced ObjectScript",
"type": "hardback",
},
{
"id": 4,
"title": "IRIS Embedded Python and You",
"type": "paperback",
},
]
Since we're getting this data from another system, we have no way of knowing how many elements will be in this list when we get it. Thankfully, both dynamic arrays and objects are iterable!
We can use the %GetIterator() method coupled with the %GetNext(.key,.value) method to iterate through each object in the array, or each top-level property of an object, to access the individual data.
set booksJSON = set booksJSON = "[{""id"": 1,""title"": ""ObjectScript for Dummies"",""type"": ""paperback""},{""id"": 2,""title"": ""Intermediate ObjectScript"",""type"": ""paperback""},{""id"": 3,""title"": ""Advanced ObjectScript"",""type"": ""hardback""},{""id"": 4,""title"": ""IRIS Embedded Python and You"",""type"": ""paperback""}]"
set booksArray = [].%FromJSON(booksJSON)
set bookIter = booksArray.%GetIterator()
while bookIter.%GetNext(.key,.value) {
write !,"Book ",value.id,": ",value.title," (",value.type,")"
}
Which presents our request payload in a nice display list:
Book 1: ObjectScript for Dummies (paperback)
Book 2: Intermediate ObjectScript (paperback)
Book 3: Advanced ObjectScript (hardback)
Book 4: IRIS Embedded Python and You (paperback)
Documentation
For more information about how these entities can be used, please read the IRIS guides and class documentation:
Creating and Modifying Dynamic Entities
%Library.DynamicObject
%Library.DynamicArray