Question
· Feb 22, 2020

Convert an array to a JSON string

I need to convert an array with an unknown number of indices to a JSON string for transmission (which is why it isn't possible to iterate through it using $ORDER). Does ObjectScript provide functionality to convert an array to a JSON string?

 

Edit 1: As Joel mentioned, the array is subscripted and has an arbitrary structure like:

a(1)="a"
a(3,4)="b"
a(3,6)="c"
a(5,6,7)="d"

which needs to be converted to

{"1":"a", "3":{"4":"b","6":"c"}, "5":{"6":{"7":"d"}}}

I had considered using a combination of $Query (to traverse the array) and $Order (to retrieve property names) but was wondering if a utility method already exists.

 

IRIS for UNIX (Apple Mac OS X for x86-64) 2019.4.0DS (Build 165U) Fri Aug 30 2019 00:02:44 EDT

Discussion (6)3
Log in or sign up to continue

If you have old version of Caché, you can use %ZEN.Auxiliary.jsonProvider or %ZEN.Auxiliary.altJSONProvider, which have a bunch of useful methods, for example:
%ArrayToJSON
%WriteJSONFromArray
%WriteJSONStreamFromArray
etc.

Here are two small examples:

set array=##class(%ListOfDataTypes).%New()
for i=1:1:4000000 array.Insert("item"_i)
   
write "count = ",array.Count(),!
   
do ##class(%ZEN.Auxiliary.altJSONProvider).%WriteJSONStreamFromObject(.stream,array,,,1,"aeloq")
   
; here you can save stream to a file or send it to the client
set meta=$lb("nameA","nameB","nameC")
   
for i=1:1:4000000 set data(i)=$lb("itemA"_i,"itemB"_i,"itemC"_i)
   
do ##class(%ZEN.Auxiliary.altJSONProvider).%ArrayToJSON(.meta,.data)

We may need more clarity in order to answer this question. This is what I think you mean. You want to take an ObjectScript array of arbitrary structure, like this:

a(1)="a"
a(3,4)="b"
a(3,6)="c"
a(5,6,7)="d"

...and turn it into JSON. But what should the target JSON for this example look like? Something like this?

{"1":"a", "3":{"4":"b","6":"c"}, "5":{"6":{"7":"d"}}}

...or something different?

In any case, to loop through an ObjectScript array of arbitrary structure, you need to use $Query.

I would recommend a different structure to the JSON since an array node can hold both, a value and subnodes. This should be reflected in the JSON. So, for an array:

a(1)="A"
a(1,1)="AA"
a(1,2)="AB"
a(1,2,1)="ABA"
a(1,3,1)="ACA"
a(2,1)="BA"
a(3)="C"
a(3,1)="CA"
I suggest to generate an JSON in the form: 

{"1":["A",{"1":["AA"],"2":["AB",{"1":["ABA"]}],"3":[{"1":["ACA"]}]}],"2":[{"1":["BA"]}],"3":["C",{"1":["CA"]}]}

This may not be the most convenient for the receiving end to read, but it can hold the full information from the array. This can easily be created using a recursive procedure:

 /// Converts an arbitrary array into an JSON-Structure returned
/// as %DynamicObject. Subnodes are read recursively, so there 
/// might be a limit tothe number of levels that can be read.
///
/// Usage example:
/// >set a(1)="A"
/// >set a(1,1)="AA"
/// >set a(1,2)="AB"
/// >set a(1,2,1)="ABA"
/// >set a(1,3,1)="ACA"
/// >set a(2,1)="BA"
/// >set a(3)="C"
/// >set a(3,1)="CA"
///
/// >write $$^Array2JSON(.a).%ToJSON()
/// {"1":["A",{"1":["AA"],"2":["AB",{"1":["ABA"]}],#
/// "3":[{"1":["ACA"]}]}],"2":[{"1":["BA"]}],"3":["C",{"1":["CA"]}]}
///
/// I: &array: reference to the array
///
/// O: JSON: %DynamicObject holding the indices as keys and node
///  values and 
/// subnodes in %DynamicArrays
Array2JSON(&array) Public
{
set JSON = ##class(%DynamicObject).%New()
set key = $order(array(""))
while ( key'="" )
{
// create a new entry
set subJSON = ##class(%DynamicArray).%New()
if ( $get(array(key))'="" )
{
do subJSON.%Push(array(key))
}
if ( $order(array(key,""))'="" )
{
kill subarray
merge subarray = array(key)
set subarrayJSON = $$Array2JSON(.subarray)
do subJSON.%Push(subarrayJSON)
}
do JSON.%Set(key,subJSON)
set key = $order(array(key))
}
return JSON
}