RXS_ParseJson()

This subprocedure provides event-based parsing for JSON documents. Using this subprocedure requires a handler subprocedure.

All JSON parsing subprocedures must have the following prototype:

D JsonHandler     PR              N
D  pType                         5I 0 Const
D  pPath                              Const Like(RXS_Var64Kv_t)
D  pIndex                       10U 0 Const
D  pData                          *   Const
D  pDataLen                     10U 0 Const

The JSON parser recognizes the following element events:

Event   pType Constant   pPath Format   Example
Array Start   RXS_JSON_ARRAY   [*]   /phone[*]
Array End   RXS_JSON_ARRAY_END   [*]   /phone[*]
Object Start   RXS_JSON_OBJECT   /   /phone
Object End   RXS_JSON_OBJECT_END   /   /phone

Because the start and end events share the same path structure, you will also need to check the value of the pType parameter.

By default, the JSON parser returns all values as character data. In order to retrieve values in their respective RPG data types, you must set the value of ConvertDataToString to RXS_NO in the RXS_ParseJsonDS_t data structure. The JSON parser recognizes the following content types:

Data Type   pType Constant   RPG Data Type
Null   RXS_JSON_NULL    
Boolean   RXS_JSON_BOOLEAN   N
Integer   RXS_JSON_INTEGER   20I 0
String   RXS_JSON_STRING   A

For a detailed example of JSON parsing, please see this blog post: Parsing JSON with RPG-XML Suite 3.3.

This subprocedure can optionally return a RXS_JsonStructureDS_t data structure which can be used with the RXS JSON composition subprocedures.

Subprocedure Prototype

D RXS_ParseJson...
D                 PR                  Extproc('RXS_ParseJson') Opdesc
D                                     LikeDS(RXS_JsonStructureDS_t)

Returns an RXS_JsonStructureDS_t which can be used by the RXS JSON composition subprocedures.

D  pInput                             Like(RXS_Var16Mv_t) Const
D                                     Options(*Omit:*Varsize)

Holds the JSON data to be passed to the parsing subprocedure. Will be ignored if the Stmf subfield of the pParseJsonDS parameter is set.

D  pParseJsonDS                       LikeDS(RXS_JsonParseDS_t)

RXS_ParseJsonDS_t data structure which controls how RXS_ParseJson() functions.

Example Code

*--------------------------------------------------------------
* This example parses a simple JSON structure stored in the
* field 'JSON' and stores the data contained in fields 'id',
* 'name', and 'price' in the corresponding global fields.
*
* At a high level, the JSON is parsed into the JsonHandler()
* in small chunks. Then, a SELECT block is used to detect whether
* or not the current JSON element matches one you'd like to
* handle. If it does, you can extract the data using RXS_STR().
*
* Once the JSON has been entirely passed through JsonHandler(),
* control returns to the main portion of the program. Before
* the program ends, you should call RXS_DestroyJson() on the
* RXS_ParseJsonDS_t data structure.
*--------------------------------------------------------------
H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

 /copy QRPGLECPY,RXSCB

D JsonHandler     PR              N
D  pType                         5I 0 Const
D  pPath                              Const Like(RXS_Var64Kv_t)
D  pIndex                       10U 0 Const
D  pData                          *   Const
D  pDataLen                     10U 0 Const

D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
D JSON            S                   Like(RXS_Var64Kv_t)
D id              S                   Like(RXS_Var1Kv_t)
D name            S                   Like(RXS_Var1Kv_t)
D price           S              6P 2
 /free
   // JSON structure looks like this:
   // {
   //    "item": {
   //      "id": 7,
   //      "name": "headlight fluid",
   //      "price": 12.50
   //    }
   // }
   JSON = '{ "item": { "id": 7, "name": "headlight fluid",' +
    '"price": 12.50 } }';

   RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );
   ParseJsonDS.Handler = %Paddr( JsonHandler );
   RXS_ParseJson( JSON : ParseJsonDS );

   RXS_JobLog( 'id: %s' : id );
   RXS_JobLog( 'name: %s' : name );
   RXS_JobLog( 'price: %s' : %Char(price) );

   RXS_DestroyJson( JsonParseDS );

   *INLR = *ON;
 /end-free


P JsonHandler     B                   Export
D                 PI              N
D  pType                         5I 0 Const
D  pPath                              Const Like(RXS_Var64Kv_t)
D  pIndex                       10U 0 Const
D  pData                          *   Const
D  pDataLen                     10U 0 Const
 /free

  select;
    when pPath = '/item/id';
      // All data in the JSON being parsed is returned
      //  as a character data field. If you need the data
      //  to be in a different data type, you can use %Int()
      //  or %Dec() to further convert it as shown further
      //  below.
      id = RXS_STR( pData : pDataLen );

    when pPath = '/item/name';
      name = RXS_STR( pData : pDataLen );

    when pPath = '/item/price';
      monitor;
        price = %Dec( RXS_STR( pData : pDataLen ) : 6 : 2 );
      on-error 105;
        RXS_JobLog( 'Error converting price!' );
        return *Off;
      endmon;


  endsl;

  return *On;
 /end-free
P                 E
*--------------------------------------------------------------
* This example parses a simple JSON structure stored in the
* field 'JSON' and stores the data contained in fields 'id',
* 'name', and 'price' in the corresponding global fields.
*
* This example differs from Example 1 in that instead of
* returning all values as character data, values will be
* returned using the RPG equivalents of the JSON data types.
*
* Doing this is somewhat more complicated, and may not be useful
* in many JSON parsing situations, so this is not the default
* behavior of RXS_ParseJson().
*--------------------------------------------------------------
H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

 /copy QRPGLECPY,RXSCB

D JsonHandler     PR              N
D  pType                         5I 0 Const
D  pPath                              Const Like(RXS_Var64Kv_t)
D  pIndex                       10U 0 Const
D  pData                          *   Const
D  pDataLen                     10U 0 Const

D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
D JSON            S                   Like(RXS_Var64Kv_t)

D id              S             20I 0
D name            S                   Like(RXS_Var1Kv_t)
D price           S              6P 2
D sale            S               N

 /free
   // JSON structure looks like this:
   // {
   //    "item": {
   //      "id": 7,
   //      "name": "headlight fluid",
   //      "price": 12.50,
   //      "sale": true
   //    }
   // }
   JSON = '{ "item": { "id": 7, "name": "headlight fluid",' +
    '"price": 12.50, "sale": true } }';

   RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );
   ParseJsonDS.Handler = %Paddr( JsonHandler );
   // By default this value is RXS_YES, meaning that all data
   //  returned will be converted into character data before
   //  it is available inside JsonHandler. Setting this to RXS_NO
   //  means that JsonHandler will instead be receiving RPG data
   //  types beyond character fields, such as integers, indicators,
   //  and floating point numbers.
   ParseJsonDS.ConvertDataToString = RXS_NO;
   RXS_ParseJson( JSON : ParseJsonDS );

   RXS_JobLog( 'id: %s' : %Char(id) );
   RXS_JobLog( 'name: %s' : name );
   RXS_JobLog( 'price: %s' : %Char(price) );
   if sale;
     RXS_JobLog( 'sale: %s' : 'true' );
   else;
     RXS_JobLog( 'sale: %s' : 'false' );
   endif;

   RXS_DestroyJson( JsonParseDS );

   *INLR = *ON;
 /end-free


P JsonHandler     B                   Export
D                 PI              N
D  pType                         5I 0 Const
D  pPath                              Const Like(RXS_Var64Kv_t)
D  pIndex                       10U 0 Const
D  pData                          *   Const
D  pDataLen                     10U 0 Const
 * To return native JSON types, we have to make use of Basing pointers
 *  and pay careful attention to the pType parameter. Please note that
 *  JSON has two types of numbers - integer and float. Floating point
 *  numbers are not precise, and values returned by RXS_ParseJson
 *  for floating-point numbers may differ slighltly from how they
 *  appear in the JSON. If your JSON data is going to contain
 *  decimal values it is advised to parse JSON using the default
 *  behavior where all parsed data is converted to character data
 *  before being returned to avoid a loss of precision.
D BoolVal         S               N   Based(pData)
D IntVal          S             20I 0 Based(pData)
D FloatVal        S              8F   Based(pData)
 /free

  select;
    when pPath = '/item/id';
      // We know that the 'id' field is an integer, so we can simply
      //  do the following:
      id = IntVal;
      // Note that integers are always going to be returned as 8 byte
      //  integers, e.g. 20I 0. Using 3I 0, 5I 0, or 10I 0 will not work.

    when pPath = '/item/name';
      // 'name' is character data, and should be handled the same way
      name = RXS_STR( pData : pDataLen );

    when pPath = '/item/price';
      // 'price' is stored in the JSON as a decimal. We know from context
      //  that price is denoting a monetary value, which is a situation
      //  where the loss of precision using a floating point number brings
      //  is completely unacceptable. For example, instead of returning
      //  12.50 as we would expect, we might get 12.501 or 12.499. This
      //  would make a large difference to a business! However, as this
      //  is example code, below demonstrates returning floating point
      //  numbers:
      price = FloatVal;

    when pPath = '/item/sale';
      // We know that the 'id' field is a boolean value. It's important to
      //  know that a JSON character data value of 'true' is different from
      //  the JSON boolean data value of true. The two items below are not
      //  equivalent:
      //
      //  {
      //    "item1": 'true',
      //    "item2" : true
      //  }
      //  The RPG equivalent of a boolean data type is an indicator field.
      sale = BoolVal;
  endsl;
  return *On;
 /end-free
P                 E
*--------------------------------------------------------------
* This example parses a simple JSON structure stored in the
* field 'JSON' and stores the data contained in fields 'id',
* 'name', and 'price' in the corresponding global fields.
*
* At a high level, the JSON is parsed into the JsonHandler()
* in small chunks. Then, a SELECT block is used to detect whether
* or not the current JSON element matches one you'd like to
* handle. If it does, you can extract the data using RXS_STR().
*
* Once the JSON has been entirely passed through JsonHandler(),
* control returns to the main portion of the program. Before
* the program ends, you should call RXS_DestroyJson() on the
* RXS_ParseJsonDS_t data structure.
*--------------------------------------------------------------
H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

 /define RXSV6R1
 /copy QRPGLECPY,RXSCB

D JsonHandler     PR              N
D  pType                         5I 0 Const
D  pPath                              Const Like(RXS_Var64Kv_t)
D  pIndex                       10U 0 Const
D  pData                          *   Const
D  pDataLen                     10U 0 Const

D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
D JSON            S                   Like(RXS_Var64Kv_t)
D id              S                   Like(RXS_Var1Kv_t)
D name            S                   Like(RXS_Var1Kv_t)
D price           S              6P 2
 /free
   // JSON structure looks like this:
   // {
   //    "item": {
   //      "id": 7,
   //      "name": "headlight fluid",
   //      "price": 12.50
   //    }
   // }
   JSON = '{ "item": { "id": 7, "name": "headlight fluid",' +
    '"price": 12.50 } }';

   RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );
   ParseJsonDS.Handler = %Paddr( JsonHandler );
   RXS_ParseJson( JSON : ParseJsonDS );

   RXS_JobLog( 'id: %s' : id );
   RXS_JobLog( 'name: %s' : name );
   RXS_JobLog( 'price: %s' : %Char(price) );

   RXS_DestroyJson( JsonParseDS );

   *INLR = *ON;
 /end-free


P JsonHandler     B                   Export
D                 PI              N
D  pType                         5I 0 Const
D  pPath                              Const Like(RXS_Var64Kv_t)
D  pIndex                       10U 0 Const
D  pData                          *   Const
D  pDataLen                     10U 0 Const
 *
D ParsedData      S                   Like(RXS_Var1Kv_t)
 /free

  select;
    when pPath = '/item/id';
      // All data in the JSON being parsed is returned
      //  as a character data field. If you need the data
      //  to be in a different data type, you can use %Int()
      //  or %Dec() to further convert it as shown further
      //  below.
      RXS_STR( ParsedData : pData : pDataLen );
      id = ParsedData;

    when pPath = '/item/name';
      RXS_STR( id : pData : pDataLen );
      name = ParsedData;

    when pPath = '/item/price';
      ParsedData = RXS_STR( pData : pDataLen );
      monitor;
        price = %Dec( ParsedData : 6 : 2 );
      on-error 105;
        price = 0;
        RXS_JobLog( 'Error converting price!' );
        return *Off;
      endmon;
  endsl;
  return *On;
 /end-free
P                 E
*--------------------------------------------------------------
* This example parses a simple JSON structure stored in the
* field 'JSON' and stores the data contained in fields 'id',
* 'name', and 'price' in the corresponding global fields.
*
* This example differs from Example 1 in that instead of
* returning all values as character data, values will be
* returned using the RPG equivalents of the JSON data types.
*
* Doing this is somewhat more complicated, and may not be useful
* in many JSON parsing situations, so this is not the default
* behavior of RXS_ParseJson().
*--------------------------------------------------------------
H DFTACTGRP(*NO) BNDDIR('RXSBND') ACTGRP(*CALLER)

 /define RXSV6R1
 /copy QRPGLECPY,RXSCB

D JsonHandler     PR              N
D  pType                         5I 0 Const
D  pPath                              Const Like(RXS_Var64Kv_t)
D  pIndex                       10U 0 Const
D  pData                          *   Const
D  pDataLen                     10U 0 Const

D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)
D JSON            S                   Like(RXS_Var64Kv_t)

D id              S             20I 0
D name            S                   Like(RXS_Var1Kv_t)
D price           S              6P 2
D sale            S               N

 /free
   // JSON structure looks like this:
   // {
   //    "item": {
   //      "id": 7,
   //      "name": "headlight fluid",
   //      "price": 12.50,
   //      "sale": true
   //    }
   // }
   JSON = '{ "item": { "id": 7, "name": "headlight fluid",' +
    '"price": 12.50, "sale": true } }';

   RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );
   ParseJsonDS.Handler = %Paddr( JsonHandler );
   // By default this value is RXS_YES, meaning that all data
   //  returned will be converted into character data before
   //  it is available inside JsonHandler. Setting this to RXS_NO
   //  means that JsonHandler will instead be receiving RPG data
   //  types beyond character fields, such as integers, indicators,
   //  and floating point numbers.
   ParseJsonDS.ConvertDataToString = RXS_NO;
   RXS_ParseJson( JSON : ParseJsonDS );

   RXS_JobLog( 'id: %s' : %Char(id) );
   RXS_JobLog( 'name: %s' : name );
   RXS_JobLog( 'price: %s' : %Char(price) );
   if sale;
     RXS_JobLog( 'sale: %s' : 'true' );
   else;
     RXS_JobLog( 'sale: %s' : 'false' );
   endif;

   RXS_DestroyJson( JsonParseDS );

   *INLR = *ON;
 /end-free


P JsonHandler     B                   Export
D                 PI              N
D  pType                         5I 0 Const
D  pPath                              Const Like(RXS_Var64Kv_t)
D  pIndex                       10U 0 Const
D  pData                          *   Const
D  pDataLen                     10U 0 Const
 * To return native JSON types, we have to make use of Basing pointers
 *  and pay careful attention to the pType parameter. Please note that
 *  JSON has two types of numbers - integer and float. Floating point
 *  numbers are not precise, and values returned by RXS_ParseJson
 *  for floating-point numbers may differ slighltly from how they
 *  appear in the JSON. If your JSON data is going to contain
 *  decimal values it is advised to parse JSON using the default
 *  behavior where all parsed data is converted to character data
 *  before being returned to avoid a loss of precision.
D BoolVal         S               N   Based(pData)
D IntVal          S             20I 0 Based(pData)
D FloatVal        S              8F   Based(pData)
 /free

  select;
    when pPath = '/item/id';
      // We know that the 'id' field is an integer, so we can simply
      //  do the following:
      id = IntVal;
      // Note that integers are always going to be returned as 8 byte
      //  integers, e.g. 20I 0. Using 3I 0, 5I 0, or 10I 0 will not work.

    when pPath = '/item/name';
      // 'name' is character data, and should be handled the same way
      name = %Str( pData : pDataLen );

    when pPath = '/item/price';
      // 'price' is stored in the JSON as a decimal. We know from context
      //  that price is denoting a monetary value, which is a situation
      //  where the loss of precision using a floating point number brings
      //  is completely unacceptable. For example, instead of returning
      //  12.50 as we would expect, we might get 12.501 or 12.499. This
      //  would make a large difference to a business! However, as this
      //  is example code, below demonstrates returning floating point
      //  numbers:
      price = FloatVal;

    when pPath = '/item/sale';
      // We know that the 'id' field is a boolean value. It's important to
      //  know that a JSON character data value of 'true' is different from
      //  the JSON boolean data value of true. The two items below are not
      //  equivalent:
      //
      //  {
      //    "item1": 'true',
      //    "item2" : true
      //  }
      //  The RPG equivalent of a boolean data type is an indicator field.
      sale = BoolVal;
  endsl;
  return *On;
 /end-free
P                 E

Data Structures

D RXS_ParseJsonDS_t...
D                 DS                  Qualified Template Inz
 
D   ReturnedErrorInfo...
D                                     LikeDS(RXS_ReturnedErrorInfoDS_t) Inz
 
D   DataStructureType...
D                                5I 0 Inz(RXS_DS_TYPE_PARSEJSON)

Internal use only

D   OnErrorMessageType...
D                                5I 0
 
D   Handler...
D                                 *   Procptr

Holds a PROCPTR to a JsonHandler subprocedure to handle all possible parsed JSON events.

D   InputPointer...
D                                 *   Inz(*Null)

Internal use only

D   InputLength...
D                               10U 0 Inz(0)

Internal use only

D   InputCcsid...
D                               10I 0 Inz(RXS_CCSID_JOB)

Specifies the CCSID of the JSON being parsed.

Default Value: Job CCSID

D   OutputCcsid...
D                               10I 0 Inz(RXS_CCSID_JOB)

Specifies the CCSID the parsed data will be converted to.

Default Value: Job CCSID

D   Stmf...
D                                     Like(RXS_Var8Kv_t)

Specifies an IFS path to a JSON file to parse instead of the pInput parm.

D   ConvertDataToString...
D                                 N   Inz(RXS_YES)

Internal use only