Question
Akshat Vora · 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

00
5 0 6 527
Log in or sign up to continue

Replies

I would expect %JSONAdapter.%JSONExportToString() should be the function to serialize arrays to string; 

extend or wrap your array into a object that inherits from %JSON.Adaptor and serialize it...

btw. the Array Objects do have "Next or GetAt" function to iterate as well (look at the matching classdocumentation)

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
}