Hi Evgeny,

The current GitHub version will only serialise and deserialise to and from class based objects.

I do however have several other solutions in the unofficial version which will efficiently serialise and deserialise to and from globals. I also have a pollyfill solution for DynamicObject and DynamicArray that uses a type mixer class that would allow older versions of Cache to work with these classes now.

However, I've not used these in production, only unit tested. I am happy to release them if there is a need / someone is willing to collaborate on production level testing and debugging.

I think you will find this is as short as it can possibly go before it starts to become unmaintainable...


ii=1:1:100 {
    s i=$tr($t(words+$s(ii#15=0:1,ii#3=0:2,ii#5=0:3,1:4)),"; ")
    i i="ii" i=@i
    w !,i



Hi David,

In general +1 for the generic boiler plate approach.

In terms of a generic JSON solution is could do with some additional type checks to make sure they are output correctly, e.g. booleans as true / false / null, empty numbers being returned as null and not empty string etc.

So just for booleans as an untested example you might replace...

set $PROPERTY(tRow,tColumnName) = $PROPERTY(rSet,tColumnName)

with this..

do tRow.%Set(tColumnName,$PROPERTY(rSet,tColumnName),$Select(tColumn.clientType=16:"boolean",1:""))

The alternative, if just going from SQL to a serialised JSON string could be to just use SQL and JSON_ARRAYAGG as per the examples here...


Hi David,

It's a little unclear if you are trying to conditionally serve static files, conditionally generate static files or something else. Do you have a more specific use case to help answer the question?

Hi Michael,

1. Copy the entire first source OBX to the first target OBX
2. Loop over the source OBX group
3. In the loop, if the loop key is greater than 1, append the source OBX 5 value to the first target OBX 5 with a preceding delimiter

It should look something like this...

If the source is...


Then your target will output as...

OBX|1||||Alpha Bravo Charlie|

Yes, the semantics of my description are not perfect.

I should probably have used the term "resolved canonical form" in this particular instance.

You can access the rule name with aux.RuleReason in the transform which provides a way to pass in a delimited value.

Another option is to chain transformations together within the same send action. So you could have a second transformation for each target that does nothing but assign a hard coded value such as MGH and MGP to your facility codes.

To chain the transformations just select a second transformation from the data transform selector, or just add the name of the transformation in a comma delimited list, e.g.



There are only strings in ObjectScript.

We can peek at the $ZTH return with zzdump to prove this...

USER>zzdump $zth("00:00:00.1")
0000: 30 2E 31

USER>zzdump $zth("00:00:01.1")
0000: 31 2E 31

I think the ZWRITE command is confusing matters a little by displaying quotes around stringy values that are not canonical numbers. It implies that there is a concept of type when there is not.

> shouldn't $ZTH be consistent in its return? All numbers in canonical form, not just those greater than or equal to 1?

It's a valid point, if a function should only ever return a stringy value in canonical form then perhaps it should always quit +val.

In terms of heartburn with $ORDER etc.

Subsrcript values are inserted into a varaible / global in a way that they are automatically sorted, first in canonical form and second (as I understand) in byte order, so stringy non canonical numbers will still be sorted, but they will apear after canonical numbers and before alpha characters.

Without knowing this its a common trip hazzard to insert fractional numbers with a leading zero and see them appear out of sequence. Which is where prefixing the value with a plus will resolve the sort problem at source.

The same problem can also appear in reverse. You might have strings that contain numbers, but you want them evaluated (sorted) as strings and not as canonical numbers.

We can see both in action with the Persistent class. If you create an index on a %Float property then it will canonicalise the string to ensure that all fractional numbers appear in the correct index sequence. If the property is a %String, then it will prefix all values with a space which is why you often see leading spaces in indexes. This ensures that all numbers (canonical or not) appear in byte order along with the strings.

Bottom line, we only have strings, and stringy numbers are only considered numbers when they are in canonical form or converted to a canonical form during a numerical operation.

Hi David,

The ruleset including assign actions are evaluated before the send actions.

In this instance, if both rules are true then the first assign will be overwritten by the second assign, before both send actions are processed.

I guess you could work around this by having two different property names and testing for the existence of a value in the DTL.

It feels like there is a better solution altogether to suggest, but its not clear what the real world use case is here.