Example 5

This example program demonstrates the basic structure of an RPG program utilizing RXS to parse JSON data.

**FREE
Ctl-Opt ActGrp(*Caller) BndDir('RXSBND') Option(*NoDebugIO) ExtBinInt(*Yes) DecEdit('0.')
  Text('Ex. 5 - JSON Parse');

/COPY QRPGLECPY,RXSCB

Dcl-S JSON Like(RXS_Var64Kv_t);

Dcl-Ds ParseJsonDS LikeDS(RXS_ParseJsonDS_t);

JSON = '{ "glossary": { "title": "example glossary", "GlossDiv": { '
     + '"title": "S", "GlossList": [ { "ID": "SGML", "SortAs": "SGML", '
     + '"GlossTerm": "Standard Generalized Markup Language", "Acronym": '
     + '"SGML", "Abbrev": "ISO 8879:1986", "GlossDef": "A meta-markup '
     + 'language, used to create markup languages such as DocBook.", '
     + '"GlossSeeAlso": ["GML", "XML", "markup", "test"] } ] } } }';

monitor;
  // Use RXS_ResetDS to ensure that RXS templated data structures are
  //  properly configured before use
  RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );

  // The JSON parsing in RXS functions via an "event-based" method.
  //  Basically, you will create a "handler" subprocedure that will
  //  be called by RXS_ParseJson once for each "event" detected in
  //  the JSON being parsed. An event is one of the following:
  //  - beginning of an JSON Array (RXS_JSON_ARRAY_BEGIN)
  //  - end of an JSON Array (RXS_JSON_ARRAY_END)
  //  - beginning of an JSON Object (RXS_JSON_OBJECT_BEGIN)
  //  - end of an JSON Object (RXS_JSON_OBJECT_END)
  //  - a JSON string value (RXS_JSON_STRING)
  //  - a JSON integer value (RXS_JSON_INTEGER)
  //  - a JSON double (float) value (RXS_JSON_DOUBLE)
  //  - a JSON boolean (indicator) value (RXS_JSON_BOOLEAN)
  //  - a JSON null value (RXS_JSON_NULL)
  // By default for convenience any numeric, boolean, or null values
  //  will be converted into character strings and passed as RXS_JSON_STRING
  //  events. This can be controlled with a subfield within ParseJsonDS.
  // Inside the "handler" subprocedure (named JSONHandler here), you
  //  will receive a number of parameters that describe the data
  //  associated with the event detected. JSONHandler() explains this
  //  in more detail
  ParseJsonDS.Handler = %Paddr( JSONHandler );

  RXS_ParseJson( JSON : ParseJsonDS );
on-error;
endmon;

*INLR = *ON;
return;


Dcl-Proc JSONHandler;
  Dcl-Pi *N Ind;
    pType Int(5) Const;
    pPath Like(RXS_Var64Kv_t) Const;
    pIndex Uns(10) Const;
    pData Pointer Const;
    pDataLen Uns(10) Const;
  End-Pi;

  Dcl-S ParsedData Like(RXS_Var1Kv_t);

  // JSONHandler must always be specified with the exact same prototype
  //  - the same number of parameters, the same number of field types, in
  //  the same order, etc. The parameters are:
  //
  // - pType = the event type, e.g. RXS_JSON_STRING, RXS_JSON_ARRAY_BEGIN,
  //    etc.
  // - pPath = XPath like syntax that indicates where in the JSON
  //    structure being parsed we are located. You can "drill down" with
  //    slashes (e.g. /object1/field1 corresponds to a 'field1' inside
  //    a parent object named 'object1', and you can indicate when a JSON
  //    JSON Array is present via [*] - further explanation below.
  // -pIndex = If the JSON object/event we're handling is a child of a
  //    JSON Array, this will indicate the 'index", e.g. the position of
  //    this object within the array, starting from a value of 1. This
  //    parameter is not relevant for objects/events not inside an array
  // -pData = Pointer to the actual data returned. This will only be set
  //    for RXS_JSON_STRING, RXS_JSON_INTEGER, RXS_JSON_DOUBLE, and
  //    RXS_JSON_BOOLEAN. It is technically also set for RXS_JSON_NULL,
  //    but will be an RPG *Null value. For RXS_JSON_OBJECT,
  //    RXS_JSON_OBJECT_END, RXS_JSON_ARRAY, and RXS_JSON_ARRAY_END,
  //    this will also be *Null as there isn't any data associated with
  //    these events.
  // -pDataLen = Length of the object. Typically this is used as the
  //    length of pData as a call to RXS_STR as seen below, but pDataLen
  //    is also set to either the total length of a JSON Array or the
  //    number of "first tier" fields in a JSON Object

  select;

    // For JSON parsing with RXS, a [*] being part of the path indicates
    //  that an array is involved. If you need to know the specific
    //  position in the array you can look at the pIndex value. Note that
    //  for nested arrays pIndex will show the position of the deepest
    //  JSON Array in the path.

    when pPath = '/'
     and pType = RXS_JSON_OBJECT;
      RXS_JobLog( 'Root object (length %s)' : %Char(pDataLen) );

    when pPath = '/glossary'
     and pType = RXS_JSON_OBJECT;
      RXS_JobLog( 'glossary object (length %s)' : %Char(pDataLen) );

    when pPath = '/glossary/title'
     and pType = RXS_JSON_STRING;
      ParsedData = RXS_STR( pData : pDataLen );
      RXS_JobLog( 'glossary title: %s' : ParsedData );

    when pPath = '/glossary/GlossDiv/title'
     and pType = RXS_JSON_STRING;
      ParsedData = RXS_STR( pData : pDataLen );
      RXS_JobLog( 'GlossDiv title: %s' : ParsedData );

    when pPath = '/glossary/GlossDiv/GlossList[*]'
     and pType = RXS_JSON_ARRAY;
      RXS_JobLog( 'Begin GlossList Array (length %s)'
                : %Char(pDataLen) );

    when pPath = '/glossary/GlossDiv/GlossList[*]/ID'
     and pType = RXS_JSON_STRING;
      ParsedData = RXS_STR( pData : pDataLen );
      RXS_JobLog( 'GlossList Array#%s ID: %s'
                : %Char(pIndex) : ParsedData );

    when pPath = '/glossary/GlossDiv/GlossList[*]/SortAs'
     and pType = RXS_JSON_STRING;
      ParsedData = RXS_STR( pData : pDataLen );
      RXS_JobLog( 'GlossList Array#%s SortAs: %s'
                : %Char(pIndex) : ParsedData );

    when pPath = '/glossary/GlossDiv/GlossList[*]/GlossTerm'
     and pType = RXS_JSON_STRING;
      ParsedData = RXS_STR( pData : pDataLen );
      RXS_JobLog( 'GlossList Array#%s GlossTerm: %s'
                : %Char(pIndex) : ParsedData );

    when pPath = '/glossary/GlossDiv/GlossList[*]/Acronym'
     and pType = RXS_JSON_STRING;
      ParsedData = RXS_STR( pData : pDataLen );
      RXS_JobLog( 'GlossList Array#%s Acronym: %s'
                : %Char(pIndex) : ParsedData );

    when pPath = '/glossary/GlossDiv/GlossList[*]/Abbrev'
     and pType = RXS_JSON_STRING;
      ParsedData = RXS_STR( pData : pDataLen );
      RXS_JobLog( 'GlossList Array#%s Abbrev: %s'
                : %Char(pIndex) : ParsedData );

    when pPath = '/glossary/GlossDiv/GlossList[*]/GlossDef'
     and pType = RXS_JSON_STRING;
      ParsedData = RXS_STR( pData : pDataLen );
      RXS_JobLog( 'GlossList Array#%s GlossDef: %s'
                : %Char(pIndex) : ParsedData );

    when pPath = '/glossary/GlossDiv/GlossList[*]/GlossSeeAlso[*]'
     and pType = RXS_JSON_ARRAY;
      RXS_JobLog( 'Begin GlossSeeAlso Array (length %s)'
                : %Char(pDataLen) );

    when pPath = '/glossary/GlossDiv/GlossList[*]/GlossSeeAlso[*]'
     and pType = RXS_JSON_STRING;
      ParsedData = RXS_STR( pData : pDataLen );
      RXS_JobLog( 'GlossSeeAlso Array#%s value: %s'
                : %Char(pIndex) : ParsedData );

    when pPath = '/glossary/GlossDiv/GlossList[*]/GlossSeeAlso[*]'
     and pType = RXS_JSON_ARRAY_END;
      RXS_JobLog( 'End GlossSeeAlso Array (length %s)'
                : %Char(pDataLen) );

    when pPath = '/glossary/GlossDiv/GlossList[*]'
     and pType = RXS_JSON_ARRAY_END;
      RXS_JobLog( 'End GlossList Array (length %s)'
                : %Char(pDataLen) );

  endsl;

  // If you want to stop parsing early for whatever reason, return
  // RXS_JSON_STOP_PARSING

  return RXS_JSON_CONTINUE_PARSING;
End-Proc;
 
     H DFTACTGRP(*NO) ACTGRP(*CALLER) BNDDIR('RXSBND') OPTION(*NODEBUGIO)
     H EXTBININT(*YES) DECEDIT('0.')
     H TEXT('Fixed Format Ex. 5 - JSON Parse')

      /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 JSON            S                   Like(RXS_Var64Kv_t)

     D ParseJsonDS     DS                  LikeDS(RXS_ParseJsonDS_t)

      /FREE

       JSON = '{ "glossary": { "title":'
            + ' "example glossary", "GlossDiv": { "title": "S", "'
            + 'GlossList": [ { "ID": "SGML", "SortAs": "SGML", "'
            + 'GlossTerm": "Standard Generalized Markup Language", "Ac'
            + 'ronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDe'
            + 'f": "A meta-markup language, used to create markup langua'
            + 'ges such as DocBook.", "GlossSeeAlso": ["GML", "XML"'
            + ', "markup", "test"] } ] } } }';

       monitor;
         // Use RXS_ResetDS to ensure that RXS templated data structures are
         //  properly configured before use
         RXS_ResetDS( ParseJsonDS : RXS_DS_TYPE_PARSEJSON );

         // The JSON parsing in RXS functions via an "event-based" method.
         //  Basically, you will create a "handler" subprocedure that will
         //  be called by RXS_ParseJson once for each "event" detected in
         //  the JSON being parsed. An event is one of the following:
         //  - beginning of an JSON Array (RXS_JSON_ARRAY_BEGIN)
         //  - end of an JSON Array (RXS_JSON_ARRAY_END)
         //  - beginning of an JSON Object (RXS_JSON_OBJECT_BEGIN)
         //  - end of an JSON Object (RXS_JSON_OBJECT_END)
         //  - a JSON string value (RXS_JSON_STRING)
         //  - a JSON integer value (RXS_JSON_INTEGER)
         //  - a JSON double (float) value (RXS_JSON_DOUBLE)
         //  - a JSON boolean (indicator) value (RXS_JSON_BOOLEAN)
         //  - a JSON null value (RXS_JSON_NULL)
         // By default for convenience any numeric, boolean, or null values
         //  will be converted into character strings and passed as
         //  RXS_JSON_STRING events. This can be controlled with a subfield
         //  within ParseJsonDS.
         // Inside the "handler" subprocedure (named JSONHandler here), you
         //  will receive a number of parameters that describe the data
         //  associated with the event detected. JSONHandler() explains this
         //  in more detail
         ParseJsonDS.Handler = %Paddr( JSONHandler );

         RXS_ParseJson( JSON : ParseJsonDS );
       on-error;
       endmon;

       *INLR = *ON;
       return;
      /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
       // JSONHandler must always be specified with the exact same prototype
       //  - the same number of parameters, the same number of field types, in
       //  the same order, etc. The parameters are:
       //
       // - pType = the event type, e.g. RXS_JSON_STRING, RXS_JSON_ARRAY_BEGIN,
       //    etc.
       // - pPath = XPath like syntax that indicates where in the JSON
       //    structure being parsed we are located. You can "drill down" with
       //    slashes (e.g. /object1/field1 corresponds to a 'field1' inside
       //    a parent object named 'object1', and you can indicate when a JSON
       //    JSON Array is present via [*] - further explanation below.
       // -pIndex = If the JSON object/event we're handling is a child of a
       //    JSON Array, this will indicate the 'index", e.g. the position of
       //    this object within the array, starting from a value of 1. This
       //    parameter is not relevant for objects/events not inside an array
       // -pData = Pointer to the actual data returned. This will only be set
       //    for RXS_JSON_STRING, RXS_JSON_INTEGER, RXS_JSON_DOUBLE, and
       //    RXS_JSON_BOOLEAN. It is technically also set for RXS_JSON_NULL,
       //    but will be an RPG *Null value. For RXS_JSON_OBJECT,
       //    RXS_JSON_OBJECT_END, RXS_JSON_ARRAY, and RXS_JSON_ARRAY_END,
       //    this will also be *Null as there isn't any data associated with
       //    these events.
       // -pDataLen = Length of the object. Typically this is used as the
       //    length of pData as a call to RXS_STR as seen below, but pDataLen
       //    is also set to either the total length of a JSON Array or the
       //    number of "first tier" fields in a JSON Object

       select;

         // For JSON parsing with RXS, a [*] being part of the path indicates
         //  that an array is involved. If you need to know the specific
         //  position in the array you can look at the pIndex value. Note that
         //  for nested arrays pIndex will show the position of the deepest
         //  JSON Array in the path.

         when pPath = '/'
          and pType = RXS_JSON_OBJECT;
            RXS_JobLog( 'Root object (length %s)' : %Char(pDataLen) );

         when pPath = '/glossary'
          and pType = RXS_JSON_OBJECT;
            RXS_JobLog( 'glossary object (length %s)' : %Char(pDataLen) );

         when pPath = '/glossary/title'
          and pType = RXS_JSON_STRING;
            ParsedData = RXS_STR( pData : pDataLen );
            RXS_JobLog( 'glossary title: %s' : ParsedData );

         when pPath = '/glossary/GlossDiv/title'
          and pType = RXS_JSON_STRING;
            ParsedData = RXS_STR( pData : pDataLen );
            RXS_JobLog( 'GlossDiv title: %s' : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]'
          and pType = RXS_JSON_ARRAY;
            RXS_JobLog( 'Begin GlossList Array (length %s)'
                      : %Char(pDataLen) );

         when pPath = '/glossary/GlossDiv/GlossList[*]/ID'
          and pType = RXS_JSON_STRING;
            ParsedData = RXS_STR( pData : pDataLen );
            RXS_JobLog( 'GlossList Array#%s ID: %s'
                      : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/SortAs'
          and pType = RXS_JSON_STRING;
            ParsedData = RXS_STR( pData : pDataLen );
            RXS_JobLog( 'GlossList Array#%s SortAs: %s'
                      : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/GlossTerm'
          and pType = RXS_JSON_STRING;
            ParsedData = RXS_STR( pData : pDataLen );
            RXS_JobLog( 'GlossList Array#%s GlossTerm: %s'
                      : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/Acronym'
          and pType = RXS_JSON_STRING;
            ParsedData = RXS_STR( pData : pDataLen );
            RXS_JobLog( 'GlossList Array#%s Acronym: %s'
                      : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/Abbrev'
          and pType = RXS_JSON_STRING;
            ParsedData = RXS_STR( pData : pDataLen );
            RXS_JobLog( 'GlossList Array#%s Abbrev: %s'
                      : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/GlossDef'
          and pType = RXS_JSON_STRING;
            ParsedData = RXS_STR( pData : pDataLen );
            RXS_JobLog( 'GlossList Array#%s GlossDef: %s'
                      : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/GlossSeeAlso[*]'
          and pType = RXS_JSON_ARRAY;
            RXS_JobLog( 'Begin GlossSeeAlso Array (length %s)'
                      : %Char(pDataLen) );

         when pPath = '/glossary/GlossDiv/GlossList[*]/GlossSeeAlso[*]'
          and pType = RXS_JSON_STRING;
            ParsedData = RXS_STR( pData : pDataLen );
            RXS_JobLog( 'GlossSeeAlso Array#%s value: %s'
                      : %Char(pIndex) : ParsedData );

         when pPath = '/glossary/GlossDiv/GlossList[*]/GlossSeeAlso[*]'
          and pType = RXS_JSON_ARRAY_END;
            RXS_JobLog( 'End GlossSeeAlso Array (length %s)'
                      : %Char(pDataLen) );

         when pPath = '/glossary/GlossDiv/GlossList[*]'
          and pType = RXS_JSON_ARRAY_END;
            RXS_JobLog( 'End GlossList Array (length %s)'
                      : %Char(pDataLen) );

        endsl;

        // If you want to stop parsing early for whatever reason, return
        // RXS_JSON_STOP_PARSING

        return RXS_JSON_CONTINUE_PARSING;
      /END-FREE
     P                 E