SBM ModScript, Part 4 - JSON

1 Likes
over 3 years ago

One useful feature of the ChaiScript engine is the support for JSON. As ModScript can be invoked directly via the Direct URL context, a JSON body could be sent by the caller and ModScript can parse it and then use it. Also, as ModScript can make REST call-outs, the fact that ChaiScript can format JSON for us empowers us to really get things done.

JSON is a convenient format for passing data in a web application as it is compact and well defined. Here is an example:

{
	"arrayOfInts": [1,2,3,4,5],
	"intData": 5,
	"doubleData": 4.5,
	"stringData": "Welcome to ModScript"
}
 
Parsing JSON

In the above JSON sample, the curly braces { ... } indicate the beginning of an object, with name-value pairs. The square braces [ ... ] indicate an array. When the ChaiScript engine parses a JSON string, integers become ints, floating points become doubles, text becomes strings. JSON arrays become Vectors. JSON objects become Maps which can store name-value pairs; it is important that the JSON not have repeating name entries in the same object as Maps can only store unique key values. With this in mind, the following is an ModScript excerpt that will parse the JSON from a string (as the JSON text has embedded quotes, we escape them with backslashes). The following script will write "Welcome to ModScript" as an Information entry in the Application Event Log:

Ext.LogInfoMsg(
"{
	\"arrayOfInts\": [1,2,3,4,5],
	\"intData\": 5,
	\"doubleData\": 4.5,
	\"stringData\": \"Welcome to ModScript\"
}".from_json()["stringData"] );

Step by step:

  • We see a function call to Ext.LogInfoMsg(), which will take the output of the inner code and write it to the Application Event Log.
  • Inside, we see a string literal with the JSON embedded in it, with quotes escaped with backslashes. (Yes, string literals can have embedded newlines in them. The resulting string will have the newlines in the string).
  • After the string literal, we see a direct call to from_json(), which parses the JSON string into a Map (because the outer most part of the JSON is a JSON object).
    • The Map has the following keys: "arrayOfInts", "intData", "doubleData", "stringData"
    • The "arrayOfInts" entry in the Map is a Vector, and each entry in the Vector is an int.
    • The "intData" entry in the Map is an int, "doubleData" is a double, "stringData" is a string.
  • Finally, we see ["stringData"], which calls the lookup operator on the Map, returning the corresponding entry. In this case, it returns the string "Welcome to ModScript", which is what gets sent to the Ext.LogInfoMsg() function call.

Notes:

  • JSON objects can have entries that are objects. In this case, after a call to from_json() you will get a Map with a key-value entry where the value is a Map.
  • Entries in a JSON array do not all need to be the same type. A JSON array could have a text entry followed by an integer entry followed by an object entry. In this case, after a call to from_json() you will get a Vector with a string, an int, and then a Map. As such, the from_json() function can parse any JSON string, as long as the JSON objects do not have duplicate key names in them.
  • The top level of the JSON does not need to be an object, it can be an array, a string, an integer, or a double.
    • The return value of from_json() may be a Map, Vector, string, int, or double.
    • You may have noticed in Part 2 that we used from_json() to take a string with comma-separated integers and turn it into a Vector. When working with Multi-Relational, Multi-Selection, or Multi-Group fields, getting the internal value will return a comma-separated list of integers with commas at the beginning and end (example: ",65,732,899,"). Trim these commas, append square braces, and you have a JSON array (example: "[65,732,899]") which from_json() can parse into a Vector of integers.

 

Formatting JSON

The ChaiScript Engine also provides a to_json() function. This can be invoked on a Map, Vector, string, int, or double, and will generate a JSON string from the object. This is very helpful, especially when the scripter is pulling text from various locations, as they do not need to worry about encoding the quotes, etc, while building the JSON string. In a future blog article about using ModScript to invoke REST calls, you will see the creation of the HTTP message body that looks like this:

["fixedFields":false, "fields":[["dbname":"TITLE"], ["dbname":"STATE"]]].to_json()

What you see here is an inline ChaiScript Map (name value pairs created with a : separator). Inside the Map is a "field" entry which is a Vector of Maps. The resulting JSON string:

{
  "fields" : [{
      "dbname" : "TITLE"
    }, {
      "dbname" : "STATE"
    }],
  "fixedFields" : false
}
 
Formatting JSON From Variant

The to_json() utility does not play nicely with Variant. This is mainly because it is not clear what JSON type the Variant should be mapped to: is it a string, an int, a string that contains an int? Due to this ambiguity, there is no easy way to make Variant play nicely with to_json(). However, Variant has a member method Variant.to_string(). With this, you can get a string from the Variant, and string has other conversion methods like string.to_int(), so chaining a Variant "v" as such: v.to_string().to_int() will give you the value of the Variant as an int. SBM 11.4 introduced many methods to avoid Variant in general, and it also added functions like Variant.to_int().

// an example for 11.3.1
Ext.LogInfoMsg(
  [ 
    Shell.Item().GetFieldValue("TITLE").to_string(), 
    Shell.Item().GetFieldValue("STATE").to_string().to_int()
  ].to_json() 
);
// equivalent example for 11.4
Ext.LogInfoMsg(
  [ 
    Shell.Item().GetFieldValueString("TITLE"), 
    Shell.Item().GetFieldValueInt("STATE") // or Shell.Item().GetFieldValue("STATE").to_int()
  ].to_json() 
);

Step by step:

  • We see a function call to Ext.LogInfoMsg(), which will take the output of the inner code and write it to the Application Event Log.
  • Inside, we see the beginning of an inline Vector
  • The first entry in the Vector will be the string text from the "TITLE" field.
  • The second entry in the Vector will be the integer value (internal value) of the "STATE" field.
  • The to_json() function is invoked on the Vector, creating a JSON string.
  • The output JSON is sent to the call to Ext.LogInfoMsg().
  • An example of the output: ["Test item 1", 27]

Notes:

  • to_json() does not work with Variant directly (or a Map/Vector which contains a Variant). A Variant can be converted to a string using Variant.to_string(), and the returned string can then be converted to int using string.to_int(). SBM 11.4 added functions like Variant.to_int() and Variant.to_double() so that the value does not first need to be converted to a string.
  • The output text from to_json() has newlines and white-space, which makes the JSON readable.
  • The use of a Map causes the entries to be reordered.

 

SBM ModScript Table of Contents

Comment List
Anonymous
Related Discussions
Recommended