JavaScript ObjectScript Notation (JSON) Builder

From CacheWiki
Revision as of 14:16, 5 March 2011 by Toucan (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Introduction

This article will provide an example of how a Caché local or global array can be serialised into a 'JavaScript ObjectScript Notation (JSON)' string. It has been written with Caché 5.0, but can be used on any later version. For more information on JSON, follow the hyperlinks below.

JSON for the masses
JSON - Wikipedia, the free encyclopedia
JSON - Introduction by Microsoft

Example

The following is an example of how to build an array in Caché that can subsequently be used to create a JSON string.

Set cachewiki("firstName")="John"
Set cachewiki("lastName")="Smith"
Set cachewiki("address","streetAddress")="21 2nd Street"
Set cachewiki("address","city")="New York"
Set cachewiki("address","state")="NY"
Set cachewiki("address","postalCode")="10021"
Set cachewiki("phoneNumbers",1)="212 555-1234"
Set cachewiki("phoneNumbers",2)="646 555-4567"

It is important to note the function that converts this array to a JSON string ignores nodes with both data and children attached (i.e. a subscript level with children cannot contain both data and object members/array elements). Furthermore, all subscript values at the next level must be of the same type (i.e. either numbers or strings), they cannot be mixed. Lastly, the first element in an array should have a subscript value of '1'.

Once you have the array defined, simply pass the array name (with subscript level, if required) into the 'fJSON' routine, see example below.

>Write $$FromArray^fJSON("cachewiki",1)
{
  "address": {
    "city": "New York",
    "postalCode": 10021,
    "state": "NY",
    "streetAddress": "21 2nd Street"
  },
  "firstName": "John",
  "lastName": "Smith",
  "phoneNumbers": [
    "212 555-1234",
    "646 555-4567"
  ]
}

The second parameter ('indent') is optional and if not defined all whitespace is removed, see example below. This reduces the amount of space required if transferring to another machine.

>Write $$FromArray^fJSON("cachewiki")
{ "address": { "city": "New York", "postalCode": 10021, "state": "NY", "streetAddress": "21 2nd Street" }, 
"firstName": "John", "lastName": "Smith", "phoneNumbers": [ "212 555-1234", "646 555-4567" ] }

You can use the following web tool to validate this output.

JSONLint - The JSON Validator

When I get the time, I will expand this routine to include a function to take a JSON string and populate a local or global array. Any suggestions or comments would be appreciated.

Code

The code to serialise a Caché array is shown below.

fJSON	;;JAVASCRIPT OBJECT NOTATION BUILDER;;V1.00;;NOV2008;;TSG;;
	; SET OF FUNCTIONS TO BUILD JSON STRINGS
	;
	; JSON (JavaScript Object Notation) is a lightweight data-interchange format. JSON is built on two structures:
	; - An object is an unordered set of name/value pairs.
	; - An array is an ordered collection of values.
	;
	; Usage:
	;
	Quit
	;
FromArray(array,indent) ;
	New qstr,qi,qarray,qnode,qdeep,qstk,qcurr,qtype,qdata,qstop,qtemp
	;
	; perform some basic validation
	If $Get(array)="" Quit ""
	If array'?1A.AN.1(1"("1.ANP1")") Quit ""
	;
	; set some initial values
	Set qstr="" If $Get(indent) Set qi=0
	;
	; open root object or array
	Set qnode=array,qstop=1
	Set qdeep=$QLength(qnode)
	Set qarray=$Query(@qnode)
	Do QNode
	;
	; navigate array
	Set qarray=array
	For  Set qarray=$Query(@qarray) Quit:qarray=""  Do QArray Quit:qarray=""
	;
	; close any open objects and arrays
	Set qdeep=-1
	Do QClose
	;
	; basic json string validation
	If $Get(qi) ZTrap "ERR"
	;
	; return json string
	Quit qstr
	;
StartObject(json,ind) ;
	Quit $$Start(json,.ind,"{")
	;
NewMember(json,ind,str) ;
	Quit $$New(json,.ind,str)
	;
EndObject(json,ind) ;
	Quit $$End(json,.ind,"}")
	;
StartArray(json,ind) ;
	Quit $$Start(json,.ind,"[")
	;
NewElement(json,ind) ;
	Quit $$New(json,.ind)
	;
EndArray(json,ind) ;
	Quit $$End(json,.ind,"]")
	;
Boolean(json,bool) ;
	Quit json_$Select(bool:"true",1:"false")
	;
Number(json,num) ;
	Quit json_+num
	;
String(json,str) ;
	If str="" Quit json_"null"
	Quit json_$Char(34)_$$Escape(str)_$Char(34)
	;
	; internal function - do not use
Start(json,ind,str) ;
	New char
	If '$Data(ind) Quit json_str_" "
	Set char=$$Back(json,1)
	If $Length(char),'(": "[char) Set json=$$Pad(json,ind,$Char(13,10))
	Set ind=ind+2
	Quit json_str_" "
	;
	; internal function - do not use
New(json,ind,str) ;
	New char
	Set char=$$Back(json,1)
	If $Length(char),'("{["[char) Set json=json_", "
	If $Data(ind) Set json=$$Pad(json,ind,$Char(13,10))
	If $Data(str) Quit $$String(json,str)_": "
	Quit json
	;
	; internal function - do not use
End(json,ind,str) ;
	If $Data(ind) Set json=json_$Char(13,10),ind=ind-2
	Quit $$Pad(json,$Get(ind,1),"",str)
	;
	; internal function - do not use
Back(str,pos) ;
	Quit $Extract(str,$Length(str)-$Get(pos))
	;
	; internal function - do not use
Pad(str,ind,pre,suf) ;
	If $Get(pre)'="" Set str=str_pre
	If $Get(ind) Set str=str_$Justify("",ind)
	If $Get(suf)'="" Set str=str_suf
	Quit str
	;
	; internal function - do not use
Escape(val) ;
	; http://www.json.org/
	;
	; " - quotation mark (34)
	; b - backspace (8)
	; f - formfeed (12)
	; n - newline (10)
	; r - carriage return (13)
	; t - horizontal tab (9)
	;
	New list
	For list="\\","//",$C(34)_$C(34),$C(8)_"b",$C(12)_"f",$C(10)_"n",$C(13)_"r",$C(9)_"t" Do
	. For  Quit:val=""  Quit:val'[$E(list)  Set val=$P(val,$E(list))_$C(2,3)_$P(val,$E(list),2,999)
	. If val[$C(3) Set val=$TR(val,$C(2,3),"\"_$E(list,2))
	. Quit
	;
	Quit val
	;
	; internal function - do not use
IsNum(value) ;
	Quit value=+value
	;
	; internal function - do not use
QArray	;
	; get current subscript level
	Set qdeep=$QLength(qarray)
	;
	; close any open objects and arrays
	Do QClose
	If qstk="" Set qarray="" Quit
	;
	; now work through each subscript level
	Set qnode=qarray,qstop=0
	For qdeep=qstk:1:qdeep Do
	. Set qnode=$Piece(qarray,",",0,qdeep)
	. If $Extract(qnode,$Length(qnode))'=")" Set qnode=qnode_")"
	. Do QNode
	. Quit
	;
	Quit
	;
	; internal function - do not use	
QClose	;
	; close any open objects and arrays 
	Set qstk="",qstop=0
	For  Set qstk=$Order(qstk(qstk),-1) Quit:qstk=""  Do  Quit:qstop
	. If qstk'>qdeep,$Piece(qstk(qstk),"->")=$QSubscript(qarray,qstk) Set qstk=qstk+1,qstop=1 Quit
	. If $Piece(qstk(qstk),"->",2)="object" Set qstr=$$End(qstr,.qi,"}")
	. If $Piece(qstk(qstk),"->",2)="array" Set qstr=$$End(qstr,.qi,"]")
	. Kill qstk(qstk)
	. Quit 
	;
	Quit	
	;
	; internal function - do not use
QNode	;
	; get current subscript value and type
	Set qcurr=$QSubscript(qarray,qdeep)
	Set qtype=$Select($$IsNum(qcurr):"array",1:"object")
	;
	; if object member
	If qtype="object",'qstop Set qstr=$$New(qstr,.qi,qcurr)
	;
	; if array element
	If qtype="array",'qstop Set qstr=$$New(qstr,.qi)
	;
	; do we need to open an object or array?
	If $Data(@qnode)\10 Do  Quit
	. Set qtype=$Select($$IsNum($QSubscript(qarray,qdeep+1)):"array",1:"object")
	. If qtype="object" Set qstr=$$Start(qstr,.qi,"{")
	. If qtype="array" Set qstr=$$Start(qstr,.qi,"[")
	. Set qstk(qdeep)=qcurr_"->"_qtype
	. Quit
	;
	; ensure array gaps are shown as empty elements
	If qtype="array",qcurr'>999 Do
	. Set qtemp=$Order(@qarray,-1)+1
	. For qtemp=qtemp:1:qcurr-1 Set qstr=$$New(qstr_$Char(34,34),.qi)
	. Quit
	;
	; add data to string
	Set qdata=@qarray
	If $$IsNum(qdata) Set qstr=$$Number(qstr,qdata) Quit
	If (qdata="true")!(qdata="false") Set qstr=qstr_qdata Quit
	Set qstr=$$String(qstr,qdata)
	;
	Quit
	;
Personal tools