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

Comments

Stefan Rieger · Feb 23, 2020

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)

0
Vitaliy Serdtsev · Feb 24, 2020

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:

<FONT COLOR="#0000ff">set </FONT><FONT COLOR="#800000">array</FONT><FONT COLOR="#000000">=</FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%ListOfDataTypes</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">%New</FONT><FONT COLOR="#000000">()
</FONT><FONT COLOR="#0000ff">for </FONT><FONT COLOR="#800000">i</FONT><FONT COLOR="#000000">=1:1:4000000 </FONT><FONT COLOR="#0000ff">d </FONT><FONT COLOR="#800000">array</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">Insert</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"item"</FONT><FONT COLOR="#000000">_</FONT><FONT COLOR="#800000">i</FONT><FONT COLOR="#000000">)
   
</FONT><FONT COLOR="#0000ff">write </FONT><FONT COLOR="#008000">"count = "</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">array</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">Count</FONT><FONT COLOR="#000000">(),!
   
</FONT><FONT COLOR="#0000ff">do </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">%ZEN.Auxiliary.altJSONProvider</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">%WriteJSONStreamFromObject</FONT><FONT COLOR="#000000">(.</FONT><FONT COLOR="#800000">stream</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">array</FONT><FONT COLOR="#000000">,,,1,</FONT><FONT COLOR="#008000">"aeloq"</FONT><FONT COLOR="#000000">)
   
</FONT><FONT COLOR="#008000">; here you can save stream to a file or send it to the client</FONT>
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)
0
Joel Solon · Feb 24, 2020

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.

0
Matthias Ruckenbauer · Feb 25, 2020

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 %DynamicArraysArray2JSON(&array) Public{set JSON = ##class(%DynamicObject).%New()set key = $order(array(""))while ( key'="" ){// create a new entryset subJSON = ##class(%DynamicArray).%New()if ( $get(array(key))'="" ){do subJSON.%Push(array(key))}if ( $order(array(key,""))'="" ){kill subarraymerge subarray = array(key)set subarrayJSON = $$Array2JSON(.subarray)do subJSON.%Push(subarrayJSON)}do JSON.%Set(key,subJSON)set key = $order(array(key))}return JSON}
0