With the release of RPG-XML Suite version 3.3, Krengel Technology is bringing JSON to the IBM i. JSON is becoming increasingly popular in web service communication, and we are eager to help our customers take full advantage of this powerful, flexible data interchange format.

In this series of tutorials, we are demonstrating the JSON composition and parsing APIs that might be used to offer or communicate with a web service. In our previous installment (read it here: https://kti.news/RXS-composing-JSON) we used RPG-XML Suite to compose a JSON object which represented contact information for a visitor at a trade show. In this tutorial, we are going to use the RXS JSON parsing API to read the data from an array of those contact objects.

The full source code for this program can be found below, and you can click the right-hand option in the toolbar to open the code in a new window. We will refer to this code throughout the post.

There are two main components to parsing JSON data with RPG-XML Suite. The first is the main procedure, which will configure the JSON parsing engine, and the second is the parsing handler subprocedure, which processes retrieved JSON data.

The handler subprocedure is the most important – and the most complex – component in a JSON parsing program. This subprocedure will be called each time a JSON node is found in your document. The handler can be configured to process events or data when specific nodes are identified. Depending on your business use case, you might want to write the data to a physical file, or perform further processing on the data. An object start event, for instance, might be used to write a header section to an output document. In this example, we’re going to write the parsed data to the job log.

All JSON parsing handler subprocedures must have the following prototype:

In order to build the parsing handler, we need to refer to the JSON document structure to identify the JSON elements and events that we want to retrieve. Here is the document we will be parsing:

The handler subprocedure is built using select blocks to control which code is executed. For each node from which we need to retrieve data, we will need to write a corresponding “when” criterion.

Note that, because our document’s root element is an array, our paths all start with “[*]“, which indicates to the parsing engine that an array will be present at that level. If our root element was an object, the paths would begin with “/” instead. As such, in order to retrieve the data from the “name” element, we would use this path:

[*]/name

The same notation is used for arrays that are nested within objects, like with the array of phone numbers in our contact object:

[*]/phone[*]

Additionally, for objects and arrays, the same path can be used to detect the start and end of the element, as well as any element data. The above path for the phone array would fire an event once for the start of the array, once for each element in the array, and once again for the end of the array. These separate events can be captured within the parsing handler subprocedure to trigger processing specific to the collection of data.

The RXSCB copybook lists constants that represent each of the JSON data types and parsing events. We can reference these constants as criteria in a select block to control which code gets triggered based on the path, the type of event, and even on the data type of the element that was parsed. Using these constants, we can build the basic structure of our parsing handler like this:

Within each “when” selector, we will be writing code to handle the different paths that might trigger that event type. For example, we have two arrays in our JSON document – the root structure array, and the child array of phone numbers in each contact object. We want to do something special for the beginning of each of these arrays, so we can write another select block to address the different paths:

We can also use the same pPath value of “[*]” within the RXS_JSON_ARRAY_END block to write out the end of the parsing operation:

For our address child object, we would like to concatenate the street address and the city and state before we print them to the job log. To accomplish this, we will use individual global fields to store the parsed address data, then write the entire formatted address to the job log during the object end event.

We’ll use the RXS_JSON_OBJECT event for the address child object to clear all of the global address component fields when the parser detects the beginning of a new address object:

After the address fields have been parsed, the RXS_JSON_OBJECT_END event will trigger for the address block, and we can perform further processing on the address data before writing it to the job log:

Note that we’re using the hexadecimal notation for the TAB character to indent the address lines – this is just for display purposes and is not required. We are also checking the length of the “aptNbr” field, so that we only write the line when an apartment number is present in the parsed data.

Because we are retrieving our JSON data in its equivalent RPG data type, we need to make selectors for each of our data types and a corresponding variable within our handler subprocedure for each non-string data type. For example, in order to retrieve the parsed data as a boolean, we need to declare an RPG boolean BASED variable:

Then we can use the following selector to capture a boolean variable event and retrieve the parsed data as an RPG indicator:

We can retrieve integer values in a similar fashion, using a BASED variable like this:

Note that any integer values returned by the JSON parser will always be of this integer data type, regardless of the actual size of the number.

Our final pType selector will be for RXS_JSON_STRING events. We’ll use RXS_STR() to retrieve the parsed data to an RPG character data variable, and either store the data for further processing or print the data to the job log. Here is an example where we retrieve the value of the “name” element and print the parsed data to the job log:

Now that our parsing handler subprocedure is complete, we will write the main subprocedure. In order to initialize the JSON parsing engine, we need to configure the RXS_ParseJson() operation using an RXS_ParseJsonDS_t data structure. First, we will specify the procedure address for our JSON handler subprocedure:

By default, the JSON parser retrieves parsed data as character (string) data. Because we want to parse our data in its native JSON data type, we will use the ConvertDataToString field to override the automatic conversion:

RXS_NO is a constant representing the RPG boolean value *Off. This setting allows the parsing engine to return different JSON data type events, which correspond to the RXS_JSON_* types referenced in our parsing handler. If this setting is left as the default value of RXS_YES (*On), every data event will be returned as an RXS_JSON_STRING event, regardless of the actual data type.

We will be parsing data from a file in the IFS, and we will use the Stmf field in our RXS_ParseJsonDS_t data structure to specify the location of our JSON document.

RXS_ParseJson() accepts two parameters, the first of which is a variable containing JSON data to be parsed. Because we are reading data from a file, we will omit the first parameter:

The parser will now read through the JSON document and call our JsonHandler subprocedure for each event that it detects. If the handler subprocedure finds a select option for the corresponding event and type, it will process the retrieved data. Otherwise, the handler subprocedure will return control to the JSON parser, which will move on to the next event.

Following the code we wrote earlier, our handler subprocedure writes the contact information to the job log. Here is the output from our program:

screen-shot-2016-11-21-at-7-46-33-am

screen-shot-2016-11-21-at-7-47-00-am

screen-shot-2016-11-21-at-7-47-12-am

screen-shot-2016-11-21-at-7-47-23-am

While this example demonstrated parsing a simple IFS file that we created manually, the same process can be used to parse JSON data that was received as a request to or response from a web service.

Does your business need to handle JSON data on your IBM i? Are you building a JSON-based web service? Contact our sales team at sales@krengeltech.com to discuss how RPG-XML Suite can meet your JSON communication requirements!

Existing RXS customers with a paid maintenance contract are eligible to upgrade to RXS 3.3 for no additional cost – contact our support team at isupport@krengeltech.com for more information!