Walkthrough
Description
The goal of this walk through is to complete a step by step example to better understand how to get started with Jsmn.
After completion, this task will parse station status JSON packets (shown below) and keep the latest information for each station. Task will have 2 variables for interfacing: a string to be populated with JSON packets and a command to parse packet.
{
"station": 1,
"status": "Completing", // "Starting", "Executing", or "Completing"
"jobId": 115
}
Declare Variables and Types
First step is to delare all variables and types needed for the rest of our walkthrough. In most cases these would be declared in .var and .typ files, but for this example they are written in C. Attached completed task includes .var and .typ files for this example. Note: IEC type are commented next to c declarations.
// Types
UserData_typ {
unsigned long station; // (UDINT)
unsigned long jobId; // (UDINT)
char[100] status; // (STRING[99])
}
StationInfo_typ {
unsigned long jobId; // (UDINT)
char[100] status; // (STRING[99])
}
// Declare parser object
jsmn_parser parser;
// Declare tokens for temporary storage
// The size of tokens array is dependent on the number of levels expected in json
// This application requires a minium array size of 3
jsmntok_t tokens[16];
// Declare user storage to be used in callbacks
UserData_typ myData;
// Declare app storage for status info
unsigned short NUM_STATIONS = 3; // (UINT)
StationInfo_typ stationInfo[NUM_STATIONS];
// Declare temporary variables
signed short parseStatus; // (INT)
// Interface variables
unsigned short parsePacket; // (BOOL)
char[501] json; // (STRING[500])
Initialize Variables
Next is to initialize the variables. Note: Variables declared in a .var file, by default will be initialized to zero. In this case those variables will not need to be set again. This code would typically be in the INIT routine but is not required to be.
// Initialize to Zero
brsmemset(&parser, 0, sizeof(parser));
brsmemset(&tokens, 0, sizeof(tokens));
brsmemset(&myData, 0, sizeof(myData));
brsmemset(&stationInfo, 0, sizeof(stationInfo));
parsePacket = 0;
// Initialize other values to default
JsmnInit(&parser);
parser.callback.pFunction = &myCallback; // We will declare this function next
parser.callback.pUserData = &myData;
// Initialize json for testing
brsstrcpy(json, "{ \
\"station\": 1, \
\"status\": \"Completing\", \
\"jobId\": 115 \
}");
Setup Callback
Next we need to declare a callback function. Callback (myCallback) function will need to handle the expected json packet shown in description, and populate myData with the values in the JSON string.
unsigned long myCallback(UserData_typ* data, jsmn_callback_data* callbackData) {
if(callbackData->Size == 0) { // This check filters out callbackData that is not a primitive value
if(brsstrcmp(callbackData->Structure[1], "station") == 0) { // Check if Key is "station"
data->station = brsatoi(callbackData->Value); // Convert value from string to Integer
}
else if(brsstrcmp(callbackData->Structure[1], "status") == 0) { // Check if Key is "status"
brsstrcpy(data->status, callbackData->Value);
}
else if(brsstrcmp(callbackData->Structure[1], "jobId") == 0) { // Check if Key is "jobId"
data->jobId = brsatoi(callbackData->Value); // Convert value from string to Integer
}
}
return 0; // No errors
}
Parse JSON
Finally we need to parse the JSON and handle the resulting information. To do this first we call JsmnParse with the configured parser. If function does not return an error, take myData and populate the cooresponding station with that information.
if(parsePacket) {
// JSON packet populated in pJson
parseStatus = JsmnParse((UDINT)&parser, json, brsstrlen(json), (UDINT)&tokens, sizeof(tokens)/sizeof(tokens[0]));
if(parseStatus >= 0) { // Check that no errors occurred
stationInfo[myData.station].jobId = myData.jobId;
brsstrcpy(stationInfo[myData.station].status, myData.status);
}
// Re-Initialize parser object
JsmnInit(&parser);
// Reset command
parsePacket = 0;
}
Callback Iterations
While parsing jsmnParse will call user defined callback (myCallback) several times each iteration with different data. Below shows what callbackData contains when passed into callback function. As you can see our callback ignores several callback iterations.
Clean-up Variables
Before parsing the next packet variables will need to be cleaned up. In this example we only care about the parser object.
// Re-Initialize parser object
JsmnInit(&parser);
Complete
Task now will now support JSON station packets. To test task, open a watch window and add: json and parsePacket. Populate json values with desired test and set parsePacket to true. Additional error handling can be added including supporting partial packets and packets missing some attributes.