Development with openABK openABK development resources

CJsonParser

class CJsonParser

The JSON parser allows you to parse a JSON formatted object. Unlike common approaches which build an object tree as result, CJsonParser iterates through the formatted string from element to element. This avoids time-consuming memory allocations and time-consuming search operations when accessing such an object tree. When extracting values, your code provides a loop and within this loop you test for the values you want to extract. Modularity is provided since you can build separate loops for each object and for each array. The decoding process consists of the following steps:

  1. Create an instance of CJsonParser and provide a char * to the formatted object
  2. Test for the values/objects/arrays you need to extract
  3. Iterate and continue with step 2

If you encounter an object or an array in step 2, you may call a sub-routine which performs these steps locally or build a further loop in the loop. This approach allows modular and nested decoding.

Construction/Creation

CJsonParser::CJsonParser (const char *pStartOfExpression);

Constructs a CJsonParser object ready to extract members and iterating. The parser can also be seen as an iterator which can provide information of the current position. pStartOfExpression is a pointer to a null-terminated (C-style) string.

Extracting Values

Since the parser behaves like an iterator, you can test for various conditions at the current position. One of the condition is to test for a member. If the current position holds the queried member, it can be extracted.

bool ExtractValue (const char *strName, std::string *pGet) const;   // extracts string from object
bool ExtractValue (const char *strName, int *pGet) const;           // extracts integer from object
bool ExtractValue (const char *strName, double *pGet) const;        // extracts double from object
bool ExtractValue (const char *strName, bool *pGet) const;          // extracts boolean from object
bool ExtractValue (const char *strName, time_t *pGet) const;        // extracts time from object
bool ExtractValue (std::string *pGet) const;                        // gets string from array
bool ExtractValue (int *pGet) const;                                // gets integer from array
bool ExtractValue (double *pGet) const;                             // gets double from array
bool ExtractValue (bool *pGet) const;                               // gets boolean from array
bool ExtractValue (time_t *pGet) const;                             // gets time from array

There are two kinds of variants: With and without a strName argument.

  • When parsing an object (which is also the case at the beginning, the root), members have a name and therefore one has to specify a name when testing for occurrence of a member at the current position. The return value is true if the current position holds the member with the specified name and type. Specify an unescaped name in strName.
  • When parsing an array, members do not have names and you can extract values without specifying names. The return value is true if the data type at the current position matches the queried type.

All of these functions return false if there is an object or an array at the current position.

If the return value is false, the content of the return value is not altered (the location at the pointer for the return value is left untouched).

Extracting a value dos not iterate. Theoretically it is possible to decode a value multiple times at the current position.

Detecting Objects

When parsing, objects may be encountered at the current position.

bool TestObject (const char *strName) const;   // returns true if current is start of array with specified name
bool TestObject (std::string *pNameGet) const; // test for object and returns name of it
bool TestObject (void) const;                  // test for object within an array

In your parsing loop, you may test if there is an object with the name strName or pNameGet. In this case you may enter a new loop for decoding the members of the nested object. In this new loop, , call the operator++() and then extract the values with ExtractValue() variants with a strName argument. If you are not interested in the content of the object, just call SkipItem().

Detecting Arrays

When parsing, arrays may be encountered at the current position.

bool TestArray (const char *strName) const;   // returns true if current is start of array with specified name
bool TestArray (std::string *pNameGet) const; // test for array and returns name of it
bool TestArray (void) const;                  // test for an array within an array

In your parsing loop, you may test if there is an array with the name strName or pNameGet. In this case you may enter a new loop for decoding the members of the nested array. In this new loop, call the operator++() and then extract the values with ExtractValue() variants without a strName argument. If you are not interested in the content of the array, just call SkipItem().

Test for End of Record

When iterating, it is necessary to test if the end of the string or the end of the object is encountered.

bool CJsonParser::IsDone (void) const;

IsDone() returns true if the end of an object or an array is encountered. When iterating within a nested object or array, IsDone() returns true at the end of the nested object or array, respectively. When parsing the root, IsDone() signals the end of the whole object. If the end of the string is encountered, IsDone() returns true.

Iterating

When decoding, you have to iterate through all elements.

CJsonParser& CJsonParser::operator++ ();

With the prefix operator++() the next element gets the current position. Iterating from one member of an object to the next and iterating from one array member to the next requires one iteration step.

When encountering a nested object or array, one iteration step is required to dive into the object or array. Then the current position is the first element in the nested object or array, respectively.

Skipping elements

bool CJsonParser::SkipItem (const char *strItemName/*=NULL*/)

If you are not interested in the members of a nested object or a nested array, use SkipItem(). It skips objects and arrays and also nested arrays and objects within it. If strItemName is provided, the item will be skipped on a name match. If the name does not match, SkipItem() does nothing. If strItemName is NULL, no name comparison and the object or array is skipped anyway.

This method does nothing and returns true, if there is no object and no array at the current position.

The return value is false if a syntax error was encountered. It is true if successfully skipped the item or successfully done nothing.

Parsing Loop

As mentioned above, parsing requires you to build a loop. Each pass of the loop requires the iteration.

CJsonParser parser(cRxBuf);
for(;!parser.IsDone();++parser)
{
  double dData;
  parser.ExtractValue("MyValue",&dData); // get data
  parser.SkipItem(); // skip unexpected array or object
}

Within the loop place a call to ExtractValue() for each element you whish to decode. After testing and/or extracting the required data, call SkipItem() to skip any unexpected array or object.

Example


char *strResponse=... // get a response string from elsewhere
CJsonParser jpEvent(strResponse);
for(;!jpEvent.IsDone();++jpEvent)  // each event
{
  if(jpEvent.TestObject("DataLists")) // is there DataLists:{
  {
    for(++jpEvent;!jpEvent.IsDone();++jpEvent)  // each data list
    {
      std::string strDaqName;
      if(jpEvent.TestArray(&strDaqName)) // if array
      {
        CAbkClientDaq *pDaq=FindDaq(strDaqName); // get the client-side daq list
        if(pDaq)
        {
          std::list::iterator iterVar; // variable in the daq list
          iterVar=pDaq->m_lstVars.begin();
          for(++jpEvent;!jpEvent.IsDone();++jpEvent) // each value
          {
            CAbkClientVar *pVar=*iterVar; // this variable gets the new data
            pDaq->OnValueFromServer(pVar,&jpEvent);
            ++iterVar;
            jpEvent.SkipItem(); // skip unexpected items
          }
        }
      } // end of data array
      jpEvent.SkipItem(); // skip unexpected items
    } // for each data list
  } // if data lists
  jpEvent.SkipItem(); // skip unexpected items
} // for each event

In this simplified example, a server response is decoded. Note that the parsers current position starts at the first element and the first for loop has no iterator at its beginning. All subsequent for loops iterate before the first pass since they have to dive into the sub-array or sub-object. At the end of each loop there is a call to SkipItem() to skip object members which are not expected.

 

openABK