Creating and Manipulating JSON
Creating json and jsonarray values from strings
The simplest way to populate a json
or jsonarray
variable is to make use of implicit casting from string
values to the json
and jsonarray
types. With the multiline string declaration this can be achieved as follows:
Code Block |
---|
|
json jsonPerson = /%
{
"name": "Jack",
"surname": "Marques",
"phoneNumber": "555-6162"
}
%/; |
Code Block |
---|
|
jsonarray jsonPeople = /%
[
{
"name": "Jack",
"surname": "Marques",
"phoneNumber": "555-6162"
},
{
"name": "Bevin",
"surname": "Sharp",
"phoneNumber": "555-0123"
}
]
%/; |
Note, that no compile time validation is done to validate that the values being converted represents correctly formatted JSON. This means that if any such conversion issues were to arise, it will only be at runtime. Any conversion errors will result in an error message being logged to the Helium Logging Service:
Code Block |
---|
< {"id":"ca7963c5-4d5d-44e9-ba65-dcbb68127b9f","key":"Runtime Error","value":"[PersonResourceV2:23] {\n \"name\": \"Jack\",\n \"surname\": \"Marques\",\n \"phoneNumber\": \"555-6162\n } could not be converted to a com.mezzanine.program.web.core.type.JsonType@577db227 value","millis":1536562229362,"appId":"a7c76024-b379-49e4-a3c0-bd2ef30d28ab","appUserId":null} |
In addition to the above conversion error, the result of the conversion will be null
. This might lead to unexpected null pointer exceptions if not handled appropriately in DSL apps.
Although casting from string
values to json
and jsonarray
might be quick and useful in some specific cases, its uses might be limited and, if the values are constructed "by hand", there is a high probability of undesirable runtime errors.
A better approach for constructing JSON values in a DSL app, is to use the supplied json
manipulation built-in functions namely jsonPut
and jsonGet
.
Manipulating json values using jsonPut and jsonGet
In order to use jsonPut
and jsonGet
a json
variable needs to be declared and instantiated. If the variable is not instantiated, any subsequent operations might lead to null pointer exceptions. For instantiation, the implicit casting between string
and json
can again be utilised:
Code Block |
---|
|
json jsonPersonObject = "{}"; |
jsonPut
can be used to add fields and values to a json
variable:
Code Block |
---|
|
jsonPersonObject.jsonPut("name", "John");
jsonPersonObject.jsonPut("surname", "Smith");
jsonPersonObject.jsonPut("luckyNumber", 13);
jsonPersonObject.jsonPut("dob", Date:fromString("1936-05-02"));
jsonPersonObject.jsonPut("age", (Date:daysBetween(Date:fromString("1936-05-02"), Mez:now()))/365);
jsonPersonObject.jsonPut("gender", GENDER.Male);
jsonPersonObject.jsonPut("height", 1.83);
jsonPersonObject.jsonPut("militaryService", true); |
The arguments required for jsonPut
is firstly the name of the json
field, followed by the value for that field.
json
values can also be nested:
Code Block |
---|
|
// JSON representing contact details
json jsonContactDetails = "{}";
jsonContactDetails.jsonPut("phoneNumber", "555-6162");
jsonContactDetails.jsonPut("emailAddress", "john.smith@gmail.com");
// Adding contact details JSON to a JSON person
jsonPersonObject.jsonPut("contactDetails", jsonContactDetails); |
Similarly, jsonGet
can be used to retrieve values:
Code Block |
---|
|
string name = jsonPersonObject.jsonGet("name");
string surname = jsonPersonObject.jsonGet("surname");
string phoneNumber = jsonPersonObject.jsonGet("phoneNumber");
int luckyNumber = jsonPersonObject.jsonGet("luckyNumber");
date dob = jsonPersonObject.jsonGet("dob");
int age = jsonPersonObject.jsonGet("age");
GENDER gender = jsonPersonObject.jsonGet("gender");
decimal height = jsonPersonObject.jsonGet("height");
bool milService = jsonPersonObject.jsonGet("militaryService");
json contactDetails = jsonPersonObject.jsonGet("contactDetails"); |
In the code segment above, note the implicit casting for the values being retrieved.
To reference a value that is nested more that one level deep, for example a person's contact detail phone number from the example above, one needs to make use of multiple jsonGet
statements and intermediate variables. Chaining references to jsonGet
is not valid syntax in the Helium DSL.
Code Block |
---|
// First we get the contact details json object and assign it to an intermediate variable
json contactDetails = jsonPersonObject.jsonGet("contactDetails");
// Now we can get specific field values for the contact details
string phoneNum = contactDetails.jsonGet("phoneNumber");
string emailAddr = contactDetails.jsonGet("emailAddress"); |
Manipulating jsonarray values
In the above example we added a single contact detail object to a person where each field represents a channel of communication. Consider an example where we want to add multiple contact details for a person. For example, a collection of emergency contacts.
At present, no built-in functions are provided to help with the construction of jsonarray
variables. Implicit casting between any primitive array and jsonarray
and between arrays of json
and jsonarray
is, however, provided:
Code Block |
---|
// Create parent as emergency contact
json parentEmergencyContact = "{}";
parentEmergencyContact.jsonPut("description", "parent");
parentEmergencyContact.jsonPut("mobileNumber", "27763300000");
// Create spouse as emergency contact
json spouseEmergencyContact = "{}";
spouseEmergencyContact.jsonPut("description", "spouse");
spouseEmergencyContact.jsonPut("mobileNumber", "27763300001");
// Create a json[] of the above
json[] emergencyContacts;
emergencyContacts.append(parentEmergencyContact);
emergencyContacts.append(spouseEmergencyContact);
// Cast it to jsonarray so we can add it to the json object
jsonarray emergencyContactsArray = emergencyContacts;
// Add it to the person object
jsonPersonObject.jsonPut("emergencyContacts", emergencyContactsArray); |
To retrieve the individual values one can make use of jsonGet
and implicit casting to json[]
:
Code Block |
---|
// Retrieve the emergency contacts
jsonarray retrievedEmergencyContacts = jsonPersonObject.jsonGet("emergencyContacts");
// Cast back to json[]
json[] convertedEmergencyContacts = retrievedEmergencyContacts;
// Iterate over or reference as normal DSL collection
foreach(json currentEmergencyContact: convertedEmergencyContacts) {
.
.
.
}
json firstEmergencyContact = convertedEmergencyContacts.get(0); |
Note that casting from jsonarray
to json[]
is a relatively expensive operation and if possible, it should be avoided for large arrays.
The example below describes the same concepts as discussed above.
Code Block |
---|
json pet1 = /%
{
"name": "Jasmine",
"type": "Dog"
}
%/;
json pet2 = /%
{
"name": "Markus",
"type": "Mole-rat"
}
%/;
json[] petsArray;
petsArray.append(pet1);
petsArray.append(pet2);
// json[] converted to jsonarray
jsonarray petsJsonArray = petsArray;
// jsonarray converted json[]
json[] convertedPetsArray = petsJsonArray; |
In the above examples the implicit casting between json[]
and jsonarray
is shown. Casting to primitive arrays is also supported:
Code Block |
---|
int[] numbersPrimitiveArray;
numbersPrimitiveArray.append(1);
numbersPrimitiveArray.append(2);
numbersPrimitiveArray.append(3);
// int[] converted to jsonarray
jsonarray numbersJsonArray = numbersPrimitiveArray;
// jsonarray converted back to int[]
int[] numbersPrimitiveArray2 = numbersJsonArray; |
Code Block |
---|
// string representing a number array in json format converted to jsonarray
jsonarray primeNumbersJsonArray = /%[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]%/;
// jsonarray converted to int[]
int[] primeNumbersPrimitiveArray = primeNumbersJsonArray; |
Reading json
and jsonarray values from the app schema
json
and jsonarray
types can also be used for object attributes. These values are represented in the app schema by the PostgresSQL jsonb type and can be retrieved in any way that other attribute values can. At present, only json
is supported as valid object attribute type. Values for json
types are represented in the app schema by the PostgresSQL jsonb type and can be retrieved in any way that other attribute values can.