In a previous tip (https://sites.google.com/site/intersystemsmv/home/a-cache-of-tips/workingwithassociatedmultivaluefieldsusingjson) we discussed how to use JSON to support Multivalue associated fields on a ZEN page. Since that time support for JSON in ZEN has improved further allowing the JSON provider to be used as a full replacement for the current data provider. This is available in 2013.1. This tip, however, is to discuss how you can use JSON for data transport needs outside of ZEN.
First What is JSON?
JSON stands for JavaScript Object Notation. It is a way to represent data structures in a lightweight and easily read and parsed format. JSON is based on the JavaScript programming language, specifically JavaScript arrays. This is one of the elements that give such flexibility to this format. It is important to remember, however, that while all JSON representations are JavaScript arrays not all JavaScript arrays are JSON. You also need to know that, although JSON is based on portions of JavaScript, it is not necessary to use that language in your projects. There are JSON parsing APIs available for just about any programming language out there. For further details on this you can go to http://www.json.org/ or http://en.wikipedia.org/wiki/JSON (among many other sites).
JSON can be viewed as essentially two types of structures.
· A collection of name/value pairs.
o Names and values may be quoted or unquoted.
· Ordered List of values
This is best seen in an example:
{
"firstName": "John",
"lastName": "Smith",
"age": 25,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": 10021
},
"phoneNumber": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "fax",
"number": "646 555-4567"
}
]
}
This would correspond to an object that has properties for firstName, lastName, and age. There is also a property call address that points to an embedded object that contains a street, city, state, and postalCode. Finally there is a collection of phoneNumber objects each of which has a type and number. Note the [] characters that surround the ordered list of phone number objects. In Caché this would most likely be implemented as a persistent class supported by two serial classes for Address and Phone Number.
As noted above Caché has had the ability to use JSON for quite some time. You can generate JSON from an Object, a Caché array and use JSON to generate an instance of a specific Caché or a generic proxy object. This works very well for supporting Web development (web pages and RESTful web services), however what if you want to use JSON internally? In 2013.1 we have expanded the capabilities of the JSON provider to allow easier and more complete access to this. In this release we have added specific method calls to take an object or Caché array and return JSON as a stream that can then be used in any program. We have also exposed the method that goes in the other direction, taking JSON and creating a Caché object. Note in earlier versions we did have some of this capability, but it was part of internal methods that were not publicly documented. The new methods are public, documented, and enhanced to provide a better interface. We have also added a new JSON provider that takes a SQL statement and generates JSON. However, this interface does not currently have a ‘write to stream’ method. This may be in 2013.2.
So let’s dig into some examples.
First the class we will use for all of these examples is show below. This is class representing a PICK style multidimensional file that contains a few discrete fields and one associated multivalue set for Phone number and type (details not relevant to the example have been deleted for brevity):
Class MVFILE.PERSON Extends (%Persistent, %MV.Adaptor, %XML.Adaptor, JSONExtension.Model, %Populate)
{
Property Age As %String(COLLATION =
"MVR",
MVATTRIBUTE = 1,
MVAUTO =
"I", MVNAME =
"AGE",
MVPROJECTED = 1,
MVTYPE =
"D", POPORDER =
1, POPSPEC =
"Integer(20,80)");
Property Birthday
As %MV.Date(MVATTRIBUTE =
8, MVAUTO =
"P", MVNAME =
"Birthday",
MVPROJECTED = 0,
MVTODISPLAY =
"D2-", MVTYPE =
"D", POPSPEC =
".PopBirthday()");
Property HAIR As %String(COLLATION =
"Space",
MVATTRIBUTE = 2,
MVNAME =
"HAIR", MVPROJECTED =
1, MVTYPE =
"D", POPSPEC =
"ValueList("",Red,Brown,Brunette,Blond,Gray"")");
Property ItemId
As %Integer(MINVAL =
1, POPSPEC =
"Counter()");
Property NAME As %String(COLLATION =
"Space",
MVATTRIBUTE = 3,
MVNAME =
"NAME", MVPROJECTED =
1, MVTYPE =
"D", POPSPEC =
"Name()");
Property PHONE As list Of %String(COLLATION =
"Space",
MVASSOCIATION =
"PHONELIST", MVATTRIBUTE =
4, MVNAME =
"PHONE",
MVPROJECTED = 1,
MVTYPE =
"D", POPSPEC =
"USPhone():3",
SQLPROJECTION =
"table", SQLTABLENAME =
"PPHONES",
XMLNIL = 1);
Property PhoneType
As list Of %String(COLLATION =
"Space",
MVASSOCIATION =
"PHONELIST", MVATTRIBUTE =
5, MVNAME =
"PHONE.TYPE",
MVPROJECTED = 1,
MVTYPE =
"D", POPORDER =
-1, POPSPEC =
"", SQLPROJECTION =
"table");
}
Here is a listing of the sample data:

Now let’s look at how to take this object and generate a JSON representation.
Here is a sample program and its output. The %WriteJSONStreamFromObject is the method that does all the work. You pass in a variable to hold the stream, an object that you want output , and some flags to control the format of the output. Note the parameters skipped give you additional options that are not necessary to achieve a simple object to JSON conversion. You will note the use of formatting characters “cbwi” which, as indicated, control the formatting of the JSON. In this case:
o c – tells the conversion to include two special elements; _class and _id to hold the class name and item id for the object being sent in JSON
o b – tells the conversion to put a cr/lf after the initial brace ({) of the JSON
o w – tells the conversion to use Windows style new line (cr/lf)
o I – tells the conversion to indent the output
* first open an object from MVFILE.PERSON
PersonObj = "MVFILE.PERSON"->%OpenId("2")
* Now generate the JSON
"%ZEN.Auxiliary.jsonProvider"->%WriteJSONStreamFromObject(jsonStream,PersonObj,,,,"cbwi")
jsonStream->OutputToDevice()
The results of this is:
{
"_class":"MVFILE.PERSON",
"_id":2,
"Age":"37",
"Birthday":"3188",
"HAIR":"Blond",
"ItemId":2,
"NAME":"Jackson,Zoe M.",
"PHONE":["388-728-9405","567-550-1468","820-550-2877"],
"PhoneType":["HOME","WORK","CELL"]
}
Note that each property is a Name/value pair and the list of Phone numbers and types are shown as an Ordered List for the Name.
This stream can now be used anywhere in a program including turning the stream into a regular string, assuming that the JSON is not longer than the maximum string size limit.
Take JSON and create a Caché object that can be saved
Assume we have a JSON string as shown below:
{"Age":"25","Birthday":"7369","HAIR":"RED","ItemId":99,"NAME":"Jackson,Zoe M.","PHONE":["203-234-1234","345-234-4533"],"PhoneType":["HOME","WORK"]}
This represents a new PERSON record whose id is 99. To import this we need to use the %ConvertJSONToObject method. Once you generate the object you still have to save it, assuming that this is a persistent object. This method takes 4 paramenters:
1. a string or stream containing a JSON object
2. The name of the Caché class that you wish to put this JSON object into. Note if you don’t provide a class name a ZEN Proxy object is created instead that will have all of the ‘named’ properties from the JSON without the need to create a representative Caché class. Of course you would not be able to save that object directly.
3. A variable that will hold the Object reference (OREF) for the newly created object
4. A boolean (1 or 0) value that indicates whether or not to ignore names in the JSON that don’t have corresponding properties in the Caché class. A 1 say ignore the missing properties and a 0 (default) tells the system to return an error on this condition.
crt "JSON string to import"
crt newJSON
crt
"%ZEN.Auxiliary.jsonProvider"->%ConvertJSONToObject(newJSON,"MVFILE.PERSON",newPerson,1)
crt "Object Properties"
crt " Name: ":newPerson->NAME
crt " Age : ":newPerson->Age
crt " Hair: ":newPerson->HAIR
SaveStatus = newPerson->%Save()
crt "Object save status: ":SaveStatus
The output from this call is:
JSON string to import
{"Age":"25","Birthday":"7369","HAIR":"RED","ItemId":99,"NAME":"Jackson,Zoe M.","PHONE":["203-234-1234","345-234-4533"],"PhoneType":["HOME","WORK"]}
Object Properties
Name: Jackson,Zoe M.
Age : 25
Hair: RED
Object save status: 1
And doing a CMQL listing of this record looks like this:

So as you can see working with JSON in Caché is quite easy and flexible.
Next week we will cover RESTful web services which are often discussed side-by-side with JSON.
Richard S Taylor
Sales Engineer
InterSystems Corporation
Office: 443-340-8614
FAX: 440-815-5805
The Keys to Breakthroughs
April 7-10 Orlando, FL Register Now