Forums >> Programming >> Proof of Concept (POC) >>
RE: Parsing JSON with RPG and JSONTOOL (using F.JSON2)




Posted:
bvstone

RE: Parsing JSON with RPG and JSONTOOL (using F.JSON2)

 
RE: Parsing JSON with RPG and JSONTOOL (using F.JSON2)

Recently we updated our JSONTOOL package to include a new service program for parsing JSON data.  The name is F.JSON2.  The main differences between this and the original F.JSON service program (which is still included in the JSONTOOL package) are:

  • 64k limit is now 16 meg
  • The ability to parse data from a stream file "directly"
  • The init() and cleanup() procedures

Assuming the data is the same as the previous post, if we were to update our application to use the new F.JSON2 service program it would look like the following:

     H DFTACTGRP(*NO) BNDDIR('BVSTOOLS')
      ****************************************************************
      * Imports
      ****************************************************************
      /COPY QCOPYSRC,P.IFSSTD
      /COPY QCOPYSRC,P.JSON2
      /COPY QCOPYSRC,P.STRINGS
      ****************************************************************
     D MAX_VEHICLES    C                   CONST(999)
      *
     D vElem           S          65535    Varying
     D vElemData       S          65535    Varying
     D errMsg          S            256
     D rc              S             10i 0
     D count           S             10i 0
     D i               S             10i 0
      *
     D v_id            S            128    DIM(MAX_VEHICLES)
     D v_vin           S            128    DIM(MAX_VEHICLES)
     D v_odo           S             13P 5 DIM(MAX_VEHICLES)
     D v_lpstate       S              2    DIM(MAX_VEHICLES)
      ********************************************************************
      /free

       #js2_init();
       #js2_setValue('stmf':'/tmp/jsontestfiles/jsondata.json');
       rc = #js2_loadStmf(errMsg);

       if (rc > 0);
         #js2_setValue('tag':'count');
         count = #st_CtoN(#js2_getDataStr(errMsg));

         for i = 1 to count;
           vElem = 'vehicle[' + %char(i) + ']';
           vElemData = vElem + ':id';
           #js2_setValue('tag':vElemData);
           v_id(i) = #js2_getDataStr(errMsg);

           vElemData = vElem + ':vin';
           #js2_setValue('tag':vElemData);
           v_vin(i) = #js2_getDataStr(errMsg);

           vElemData = vElem + ':odometer:value';
           #js2_setValue('tag':vElemData);
           v_odo(i) = #st_CtoN(#js2_getDataStr(errMsg));

           vElemData = vElem + ':licensePlate:state';
           #js2_setValue('tag':vElemData);
           v_lpstate(i) = #js2_getDataStr(errMsg);
         endfor;

       endif;

       #js2_cleanup();
       *INLR = *ON;

      /end-free

If we take a close look at this example, we see that before we do anything we call the #js2_init() subprocedure.  While this isn't "required" lets just say it's a good idea.  This subprocedure will most likely be updated in the future to do more than it already does.

Next, we see the last thing we do is call the #js2_cleanup() subprocedure.  Again, this isn't "required" but it's a good idea to do so.  It performs vital cleanup functions (memory de-allocation mainly) and resets global variables, etc.

But, the meat of the changes are the new procedures we use to load data that we want to parse.  In this example we load JSON from a stream file using this:

       #js2_setValue('stmf':'/tmp/jsontestfiles/jsondata.json');
       rc = #js2_loadStmf(errMsg);

The first subprocedure, #js2_setValue is used to set the value of the stream file we want to parse.

The next subprocedure, #js2_loadStmf() loads that data into memory.  It will return the total bytes loaded when successful, or -1 if there is an error.  The optional parameter, errMsg, is used to send back an error message if it's available.

The next big changes are how we actually parse and get the value of a JSON object.  There are actually a couple ways to do this.  One (ugly) way we provide is the use of pointers. Here's a quick example:

D JSONData        S          65535    Based(JSONData@)   
D JSONData@       S               *                      
...

#js2_setValue('tag':'objectName');       
rc = #js2_getData(JSONData@:errMsg);  

...

Now, we didn't include all the code to load the JSON data, but this gives you an idea of how this works.  If successful in finding the JSON object in the loaded string, the first parameter to #js2_getData() will return a pointer to the data.  The return value (rc in this case) will contain the length of the string.

But, because pointers aren't very fun, we also have another subprocedure that will most likely be used 99% of the time.  It will return an actual string, as shown in the new example.

           vElem = 'vehicle[' + %char(i) + ']';
           vElemData = vElem + ':id';
           #js2_setValue('tag':vElemData);
           v_id(i) = #js2_getDataStr(errMsg);

This more friendly subprocedure, #js2_getDataStr returns a string value.  If there is an error or it's not found, the value returned is blank.  But the more important thing to notice is that before we call the #js2_getData() or #js2_getDataStr() subprocedures we use the #js2_setValue to set the "tag" to the JSON object name that we want to retrieve.  In this case it will be vehicle[n]:id where n is the array element number to retrieve.

We encourage users to use these new subprocedures when they can as the old ones will most likely be deprecated and not enhanced or supported for very long.

Brad


Last edited 12/11/2014 at 18:01:01


Reply




Copyright 1983-2017 BVSTools
GreenBoard(v3) Powered by the eRPG SDK, MAILTOOL Plus!, GreenTools for Google Apps, jQuery, jQuery UI, BlockUI, CKEditor and running on the IBM i (AKA AS/400, iSeries, System i).