Created
April 6, 2018 13:02
-
-
Save gitaaron/4349183c3ec6d223a0073b87b9c2da8b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//XC1CPLLE JOB NOTIFY=&SYSUID, | |
// REGION=0M,MSGLEVEL=1,MSGCLASS=H | |
//* | |
//* Location of sample source and output datasets. | |
// SET SRC=HWTJXC1 < INPUT ... REQUIRED | |
// SET SAMPLIB=SYS1.SAMPLIB < INPUT ... REQUIRED | |
// SET LOADLIB=HWTJ.SAMPLE.LOAD < OUTPUT LOAD MODULE | |
// SET CSSLIB=SYS1.CSSLIB < CSSLIB DATA SET | |
//* C\C++ Compiler Options | |
// SET CRUN= < COMPILER RUNTIME OPTIONS | |
// SET CPARM='CXX' < COMPILER OPTIONS | |
//* Pre-linker and Linker options. | |
// SET PLANG='EDCPMSGE' < PRE-LINKER MESSAGE NAME | |
// SET PPARM='NOER' < PRE-LINKER OPTIONS | |
// SET LPARM='AMODE=31' < LINK EDIT OPTIONS | |
//* Location of C libraries required for compile, pre-link and link. | |
// SET LIBPRFX='CEE' < PREFIX FOR LIBRARY DSN | |
// SET LNGPRFX='CBC' < PREFIX FOR LANGUAGE DSN | |
//* Dataset attributes for temporary files. | |
// SET SYSLBLK='3200' < BLOCKSIZE FOR &&LOADSET | |
// SET DCB80='(RECFM=FB,LRECL=80,BLKSIZE=3200)' <DCB FOR LRECL 80 | |
// SET DCB3200='(RECFM=FB,LRECL=3200,BLKSIZE=12800)' < DCB LRECL 3200 | |
// SET TUNIT='SYSDA' < UNIT FOR TEMPORARY FILES | |
// SET TSPACE='(32000,(30,30))' < SIZE FOR TEMPORARY FILES | |
//*------------------------------------------------------------------- | |
//* COMPILE STEP: | |
//*------------------------------------------------------------------- | |
//COMPILE EXEC PGM=CCNDRVR, | |
// PARM=('&CRUN/&CPARM'),COND=(8,LT) | |
//* | |
//* STEPLIB DD specifies the location of the compiler and runtime libraries. | |
//* | |
//STEPLIB DD DSN=&LIBPRFX..SCEERUN2,DISP=SHR | |
// DD DSN=&LIBPRFX..SCEERUN,DISP=SHR | |
// DD DSN=&LNGPRFX..SCCNCMP,DISP=SHR | |
//* | |
//* SYSLIB DD specifies the location of the IBM-supplied C header (hwtjic). | |
//* | |
//SYSLIB DD DSN=SIEAHDR.H | |
//* | |
//* SYSIN DD specifies the source member to be compiled. | |
//* | |
//SYSIN DD DSN=&SAMPLIB(&SRC),DISP=SHR | |
//* | |
//* SYSLIN DD specifies the output location of the object module generated | |
//* by the compile step. | |
//* | |
//SYSLIN DD DSN=&&LOADSET,UNIT=&TUNIT., | |
// DISP=(MOD,PASS),SPACE=(TRK,(3,3)), | |
// DCB=(RECFM=FB,LRECL=80,BLKSIZE=&SYSLBLK) | |
//SYSPRINT DD SYSOUT=* | |
//* | |
//*------------------------------------------------------------- | |
//* PRE-LINKEDIT STEP: | |
//*------------------------------------------------------------- | |
//PLKED EXEC PGM=EDCPRLK, | |
// PARM='&PPARM',COND=(4,LT,COMPILE) | |
//STEPLIB DD DSN=&LIBPRFX..SCEERUN2,DISP=SHR | |
// DD DSN=&LIBPRFX..SCEERUN,DISP=SHR | |
//SYSMSGS DD DSN=&LIBPRFX..SCEEMSGP(&PLANG),DISP=SHR | |
//SYSLIB DD DSN=&LIBPRFX..SCEEOBJ,DISP=SHR,UNIT=,VOL=SER= | |
// DD DSN=&LIBPRFX..SCEECPP,DISP=SHR,UNIT=,VOL=SER= | |
//* | |
//* SYSIN DD specifies the object module generated by the compile step as | |
//* input to the prelinker. | |
//* | |
//SYSIN DD DSN=*.COMPILE.SYSLIN,DISP=(MOD,DELETE) | |
//* | |
//* SYSMOD DD specifies the output dataset to contain the prelinked object | |
//* module generated by the prelinker. | |
//* | |
//SYSMOD DD DSN=&&PLKSET,UNIT=&TUNIT.,DISP=(NEW,PASS), | |
// SPACE=&TSPACE.,DCB=(RECFM=FB,LRECL=80,BLKSIZE=&SYSLBLK) | |
//SYSOUT DD SYSOUT=* | |
//SYSPRINT DD SYSOUT=* | |
//* | |
//*------------------------------------------------------------------- | |
//* LINKEDIT STEP: | |
//*------------------------------------------------------------------- | |
//LKED EXEC PGM=HEWL,COND=((4,LT,COMPILE),(4,LT,PLKED)), | |
// PARM='&LPARM' | |
//SYSLIB DD DSN=&LIBPRFX..SCEELKED,DISP=SHR | |
// DD DSN=&LOADLIB,DISP=SHR | |
// DD DSN=&CSSLIB,DISP=SHR | |
//* | |
//* SYSLIN DD specifies the prelinked object module as input to the linker. | |
//* | |
//SYSLIN DD DSN=*.PLKED.SYSMOD,DISP=(OLD,DELETE) | |
//* | |
//* SYSLMOD DD specifies the output dataset to contain the load module | |
//* generated by the linker. | |
//* | |
//SYSLMOD DD DSN=&LOADLIB(&SRC),DISP=SHR | |
//SYSUT1 DD UNIT=&TUNIT.,SPACE=&TSPACE. | |
//SYSPRINT DD SYSOUT=* | |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** | |
* Sample JCL to execute the program. * | |
*---------------------------------------------------------------------* | |
* THIS JCL IS WRITTEN TO EXECUTE THE JSON C SAMPLE REGARDLESS OF HOW * | |
* THE SAMPLE WAS COMPILED OR LINKED. * | |
*---------------------------------------------------------------------* | |
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
//XC1RUN JOB NOTIFY=????????, | |
// MSGLEVEL=1,MSGCLASS=H | |
//EX1 EXEC PGM=HWTJXC1,REGION=1M | |
//* | |
//STEPLIB DD DSN=HWTJ.SAMPLE.LOAD,DISP=SHR | |
//SYSPRINT DD SYSOUT=* | |
//* | |
/*--------------------------------------------------------------------* | |
* REFERENCE: * | |
* See the z/OS MVS Programming: Callable Services for * | |
* High-Level Languages publication for more information * | |
* regarding the usage of JSON Parser APIs. * | |
* * | |
* Change Activity: * | |
* $L0 ME28544 HBB77A0 140930 PDJM z/OS Client Web Enablement Toolkit * | |
* JSON Parser * | |
* $L1 ME28918 HBB77A0 150228 PDLH Support for HWTJGNUV service * | |
* $01 OA49002 HBB7790 151210 PDLH: Corrected JCL in prolog * | |
* END OF SPECIFICATIONS * * * * * * * * * * * * * * * * * * * * * * * */ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <hwtjic.h> /* JSON interface declaration file */ | |
/* -----------------------------------------------------------------*/ | |
/* Type definitions for storing parsed JSON data. */ | |
/* -----------------------------------------------------------------*/ | |
typedef struct { | |
char *name; | |
char *id; | |
char *years_of_service; | |
bool is_full_time; | |
} employee_record; | |
/* -----------------------------------------------------------------*/ | |
/* Function prototypes */ | |
/* -----------------------------------------------------------------*/ | |
bool init_parser(); | |
bool parse_json_text(const char *jtext); | |
bool insert_new_employee(); | |
bool insert_boolean_entry(HWTJ_HANDLE_TYPE employee_entry); | |
bool process_employees_array(); | |
void store_and_print_employee_info(HWTJ_HANDLE_TYPE employee_entry, | |
employee_record employee); | |
void find_tester_skill(HWTJ_HANDLE_TYPE employee_entry); | |
bool do_cleanup(); | |
void traverse_json_entry(HWTJ_HANDLE_TYPE entry_handle, int indent_level); | |
void traverse_object(HWTJ_HANDLE_TYPE object_handle, int indent_level); | |
void traverse_array(HWTJ_HANDLE_TYPE array_handle, int indent_level); | |
char *find_string(HWTJ_HANDLE_TYPE object, char *search_string); | |
char *find_number(HWTJ_HANDLE_TYPE object, char *search_string); | |
bool find_boolean(HWTJ_HANDLE_TYPE object, char *search_string); | |
HWTJ_HANDLE_TYPE find_array(HWTJ_HANDLE_TYPE object, char *search_string); | |
HWTJ_HANDLE_TYPE find_object(HWTJ_HANDLE_TYPE object, char *search_string); | |
void *find_value(HWTJ_HANDLE_TYPE object, char *name, | |
HWTJ_JTYPE_TYPE expected_type); | |
void *do_get_value(HWTJ_HANDLE_TYPE object, | |
HWTJ_JTYPE_TYPE type); | |
bool has_skill(HWTJ_HANDLE_TYPE skills_array, char *job_role); | |
char *to_json_string(); | |
void display_error(char *msg); | |
void print_employee_record(employee_record *record); | |
/* ----------------------------------------------------------------- */ | |
/* Global Variables */ | |
/* ----------------------------------------------------------------- */ | |
/* A parser instance is required for all JSON callable services. */ | |
HWTJ_PARSERHANDLE_TYPE parser_instance; | |
/* A structure for storing reason codes and error descriptions. */ | |
HWTJ_DIAGAREA_TYPE diag_area; | |
/* A return code to store the result of each service call. */ | |
int return_code; | |
/* A buffer to store the serialized JSON text string. */ | |
static char *modified_json_string = NULL; | |
/* A flag variable to indiciate employee has 10 or more years of | |
* service. | |
*/ | |
bool employee_with_seniority = false; | |
/* | |
* -------------------------------------------------------------------- | |
* Sample JSON data | |
* -------------------------------------------------------------------- | |
* Our sample JSON data represents an array of employee records. The | |
* array is stored as a name:value pair, where the name of the array is | |
* the literal string "employees" and the value is the actual array | |
* data. Each array entry represents an employee record. Each entry is | |
* itself an object containing several name:value pairs. The names | |
* contained in each array entry are described below along with the data | |
* types of the corresponding values. | |
* | |
* "name": <string> | |
* "employee_id": <string> | |
* "years_of_service": <number> | |
* "skills": <array> | |
* "full_time": <boolean> | |
* "contact": <object> | |
* | |
* In this example, the JSON data is available to the program as a | |
* static string. In practice, however, the data may originate from any | |
* external source such as a text file, an HTTP request, or a database | |
* record. | |
*/ | |
static const char *JSON_TEXT = | |
"{" | |
"\"employees\": [" | |
"{" | |
"\"name\": \"john doe\"," | |
"\"employee_id\": \"ab3f560r\"," | |
"\"years_of_service\": 3," | |
"\"skills\": [" | |
"\"tester\"," | |
"\"support\"" | |
"]," | |
"\"full_time\": true," | |
"\"contact\": {" | |
"\"address\": {" | |
"\"street\": \"10 IBM Road\"," | |
"\"city\": \"Poughkeepsie\"," | |
"\"state\": \"NY\"" | |
"}" | |
"}" | |
"}," | |
"{" | |
"\"name\": \"Jane Smith\"," | |
"\"employee_id\": \"3f56ab7t\"," | |
"\"years_of_service\": 7," | |
"\"skills\": [" | |
"\"developer\"," | |
"\"manager\"" | |
"]," | |
"\"full_time\": true," | |
"\"contact\": {" | |
"\"email\": \"jsmith@email.com\"," | |
"\"phone\": \"555-444-3210\"" | |
"}" | |
"}," | |
"{" | |
"\"name\": \"Fred Flynn\"," | |
"\"employee_id\": \"ab3f569q\"," | |
"\"years_of_service\": 10," | |
"\"skills\": [" | |
"\"support\"" | |
"]," | |
"\"full_time\": false," | |
"\"contact\": {" | |
"\"email\": [" | |
"\"flynn@email.com\"," | |
"\"ff55@bedrock.net\"" | |
"]" | |
"}" | |
"}" | |
"]" | |
"}"; | |
/* Used for return value buffer for the get object entry service (hwtjgoen). */ | |
#define MIN_NAME_LENGTH 100 | |
/* Used to specify the max work area size to parser init service (hwtjinit). */ | |
#define MAX_WORKAREA_SIZE 0 /* Zero = No limit (IBM recommended value) */ | |
/* Used to specify the maximum buffer size for serialize service (hwtjseri). */ | |
#define MAX_BUFFER_SIZE 1000; | |
int main(void) { | |
/* Create a new parser instance. */ | |
if (!init_parser()) { | |
return -1; | |
} | |
/* Parse the sample JSON text. */ | |
if(!parse_json_text(JSON_TEXT)) { | |
do_cleanup(); | |
return -1; | |
} | |
/* Insert a new employee entry into the employee array. */ | |
if (!insert_new_employee()) { | |
do_cleanup(); | |
return -1; | |
} | |
/* | |
* At this point, we've parsed the sample JSON data, inserted the | |
* "Hank Hacker" employee entry into the employees array, and added the | |
* "full_time" boolean field into the new employee entry. Now, we are going | |
* to iterate through the employees array and perform additional processing | |
* on each employee entry. | |
*/ | |
if (!process_employees_array()) { | |
do_cleanup(); | |
return -1; | |
} | |
/* | |
* At this point, we've made several modifications to the original | |
* JSON data. Now suppose we need to include the JSON data as part of an HTTP | |
* request or response. We first need to obtain a JSON-formatted text string | |
* containing the modifications we've made. This can be accomplished with the | |
* hwtjseri service, which generates a syntactically-correct JSON string | |
* using the data from our parser instance. | |
*/ | |
/* Retrieve the serialized JSON text string. */ | |
modified_json_string = to_json_string(); | |
/* If serialize failed then exit. */ | |
if (modified_json_string == NULL) { | |
do_cleanup(); | |
return -1; | |
} | |
/* Free memory held by parser. */ | |
if (!do_cleanup()) { | |
return -1; | |
} | |
/* Indicate successful program exit. */ | |
return 0; | |
} | |
/* | |
* Method: init_parser | |
* | |
* Initializes the global parser_instance variable. | |
* | |
* Services Used: | |
* | |
* HWTJINIT: Provides a handle to a parse instance which is then used in | |
* subsequent service calls. The HWTJINIT service must be invoked | |
* before invoking any other parsing service. | |
*/ | |
bool init_parser() { | |
/* Declare a variable to hold the return value. */ | |
bool success = false; | |
/* Initialize the parser work area and retrieve a handle to a parser | |
* instance. | |
*/ | |
hwtjinit(&return_code, | |
MAX_WORKAREA_SIZE, /* size (in bytes) of the parser work area (input) */ | |
parser_instance, | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
printf("SUCCESS: Parser initialized.\n"); | |
success = true; | |
} else { | |
display_error("Parser initialization failed."); | |
} | |
return success; | |
} | |
/* | |
* Method: parse_json_text | |
* | |
* Parses the sample JSON data. | |
* | |
* Services Used: | |
* | |
* HWTJPARS: Builds an internal representation of the specified JSON string. | |
* This allows efficient search, traversal, and modification of | |
* the JSON data. | |
* | |
* USAGE: HWTJPARS does not make a local copy of the JSON source string. | |
* Therefore, the caller must ensure that the provided source | |
* string remains unmodified for the duration of the parser | |
* instance. If the source string is modified, subsequent service | |
* calls may result in unexpected behavior. | |
*/ | |
bool parse_json_text(const char *jtext) { | |
/* Declare a variable to hold the return variable. */ | |
bool success = false; | |
/* Parse the sample JSON text string. Parse scans the JSON text string and | |
* creates an internal representation of the JSON data suitable for search | |
* and create operations. | |
*/ | |
hwtjpars(&return_code, | |
parser_instance, | |
(char *)&jtext, /* JSON text string address(input) */ | |
strlen(jtext), /* JSON text string length (input) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
printf("SUCCESS: JSON data parsed.\n"); | |
success = true; | |
} else { | |
display_error("Unable to parse JSON data."); | |
} | |
return success; | |
} | |
/* | |
* Method: insert_new_employee | |
* | |
* Demonstrates the create service by inserting a new employee entry into the | |
* employees array and creating a new name:value pair within the new employee | |
* entry. Returns true if both create calls result in an HWTJ_OK return code. | |
* | |
* This method demonstrates two approaches for inserting new JSON data | |
* into an existing JSON text stream. First, a new employee entry is inserted | |
* into the employees array using a single call to hwtjcren. This is | |
* accomplished by passing in a stand-alone (syntactically correct) JSON | |
* text string and specifying HWTJ_JSONTEXTVALUETYPE as the EntryValueType. | |
* Note that the same result could have been achieved by issuing several calls | |
* to hwtjcren: one to insert an object and subsequent calls to insert | |
* each field using the appropriate entryValueType. | |
* | |
* Next, a boolean value is inserted into the newly created employee by | |
* specifying HWTJ_TRUEVALUETYPE as the entryValueType. Because the entry | |
* value of true is implied by the entryValueType, the entryValueAddr | |
* parm is left blank by specifying 0. The name portion of the name:value pair | |
* is specified by the entryNameAddr parm. | |
* | |
* Services Used: | |
* | |
* HWTJCREN: Create/insert a JSON entry into an existing JSON text stream. | |
*/ | |
bool insert_new_employee() { | |
/* Declare a variable to hold the return value. */ | |
bool success = false; | |
/* Create stand-alone JSON text for the new employee entry. */ | |
char *new_employee_json = | |
"{" | |
"\"name\": \"Hank Hacker\"," | |
"\"employee_id\": \"D08d45WY\"," | |
"\"years_of_service\": 25," | |
"\"skills\": [" | |
"\"developer\"," | |
"\"tester\"," | |
"\"manager\"" | |
"]," | |
"\"contact\": {" | |
"\"email\": [" | |
"\"hankh@email.com\"," | |
"\"hh87@leethax.net\"" | |
"]," | |
"\"phone\": {" | |
"\"home\": \"555-567-8912\"," | |
"\"mobile\": \"555-234-3234\"," | |
"\"work\": {" | |
"\"number\": \"555-777-3444\"," | |
"\"ext\": \"888\"" | |
"}" | |
"}" | |
"}" | |
"}"; | |
/* Retrieve the employee array located at the top-level of the JSON data. */ | |
HWTJ_HANDLE_TYPE employee_array = find_array(0, "employees"); | |
/* Confirm that the employees array was found. */ | |
if (return_code == HWTJ_OK) { | |
/* Declare a handle to hold the resulting employee object. */ | |
HWTJ_HANDLE_TYPE new_employee_handle; | |
/* HWTJCREN requires a name address of 0 in this case. */ | |
char *entryNameAddr = 0; | |
/* This call to HWTJCREN inserts the JSON data for the "Hank Hacker" | |
* employee entry directly into the employee array. The | |
* employee-array-handle parameter specifies the "insertion point" -- the | |
* object/array that will contain this entry. The entryValueType | |
* parameter specifies the format of the incoming data. In this case, | |
* HWTJ_JSONTEXTVALUETYPE is specified because the data to be inserted is | |
* valid JSON text. Note that the original JSON string is not modified by | |
* HWTJCREN. The serialize service (HWTJSERI) can be used to obtain a new | |
* JSON text string which will include the new "Hank Hacker" array entry | |
*/ | |
hwtjcren(&return_code, | |
parser_instance, | |
employee_array, /* handle to the insertion point (input) */ | |
(char *)&entryNameAddr, /* name of the object (input)*/ | |
0, /* length of the name (input) */ | |
HWTJ_JSONTEXTVALUETYPE, /* type of data to be inserted (input) */ | |
(char *)&new_employee_json, /* JSON text string address (input) */ | |
strlen(new_employee_json), /* JSON text string length (input) */ | |
&new_employee_handle, /* handle to the new entry (output) */ | |
&diag_area); | |
/* Confirm that the create was successful. */ | |
if (return_code == HWTJ_OK) { | |
printf("SUCCESS: " | |
"Inserted \"Hank Hacker\" employee entry into the employees array.\n"); | |
/* Declare and initialize a string to hold the name of the field. */ | |
char *field_name = "full_time"; | |
/* Declare a handle to the resulting name:value pair. */ | |
HWTJ_HANDLE_TYPE field_handle; | |
/* HWTJCREN requires a value address of 0 in this case. */ | |
char *entryValueAddr = 0; | |
/* This call to HWTJCREN inserts a new name:value pair into the | |
* "Hank Hacker" employee entry. We use the object handle that was | |
* returned from the prior call to HWTJCREN as the insertion point for | |
* this call. In this case, specifying HWTJ_TRUEVALUETYPE as the | |
* entryValueType serves a dual purpose: it indicates the data type of | |
* the value portion of the name:value pair, and it specifies | |
* a boolean value of true as the value. | |
*/ | |
hwtjcren(&return_code, | |
parser_instance, | |
new_employee_handle, /* handle to the "Hank Hacker" object */ | |
(char *)&field_name, /* name portion of the name:value pair */ | |
strlen(field_name), /* length of the name string */ | |
HWTJ_TRUEVALUETYPE, /* value portion of the name:value pair */ | |
(char *)&entryValueAddr, /* set to 0, value is implied */ | |
0, /* length of the value (0 in this case) */ | |
&field_handle, /* handle to the newly inserted name:value pair */ | |
&diag_area); | |
/* Confirm that the create was successful. */ | |
if (return_code == HWTJ_OK) { | |
printf("SUCCESS: " | |
"Inserted \"full_time\":true entry into the new employee entry.\n"); | |
success = true; | |
} else { | |
display_error( | |
"Unable to insert name:value pair into new employee entry."); | |
} | |
} else { | |
display_error( | |
"Unable to insert employee entry into employees array."); | |
} | |
} else { | |
display_error("Unable to retrieve employees array from JSON text."); | |
} | |
return success; | |
} | |
/* | |
* Method: process_employees_array | |
* | |
* Loops through the employees array, retrieves each name:value pair, and | |
* prints each value. The employee contact information field is only printed if | |
* the employee has experience as a tester, which is determined by the | |
* has_skill subroutine. Returns true if each service call results in an | |
* HWTJ_OK return code. | |
* | |
* Services Used: | |
* | |
* HWTJGAEN - Retrieves a handle to an array entry. | |
* HWTJGNUE - Gets the number of entries in a JSON object or array. | |
*/ | |
bool process_employees_array() { | |
/* Declare a variable to hold the return value. */ | |
bool success = false; | |
/* Get the employee array located at the top-level of the JSON data. */ | |
HWTJ_HANDLE_TYPE employee_array = find_array(0, "employees"); | |
/* Declare a variable to hold the value returned by hwtjgnue. */ | |
int num_of_employees = 0; | |
/* Use HWTJGNUE to get the number of entries in the employees array. This | |
* service is used in conjunction with the HWTJGAEN service to process each | |
* entry in the employees array. | |
*/ | |
hwtjgnue(&return_code, | |
parser_instance, | |
employee_array, /* array or object handle (input) */ | |
&num_of_employees, /* number of entries in the array/object (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Declare an array of employee_record structs. */ | |
employee_record employees[num_of_employees]; | |
/* Declare a handle to hold the current array entry being processed. */ | |
HWTJ_HANDLE_TYPE employee_entry; | |
/* Use the value returned by hwtjgnue to loop thru the employees array. */ | |
for (int i=0; i < num_of_employees && return_code == HWTJ_OK; i+=1) { | |
/* Retrieve the i-th entry from the employee array. */ | |
hwtjgaen(&return_code, | |
parser_instance, | |
employee_array, /* handle to the employee array (input) */ | |
i, /* index into the employee array (input) */ | |
&employee_entry, /* handle to the array entry (output) */ | |
&diag_area); | |
/* Extract the employee information from the JSON data and display it. */ | |
store_and_print_employee_info(employee_entry, employees[i]); | |
if (employee_with_seniority) { | |
printf("ALERT! Employee has 10 or more years of service.\n"); | |
employee_with_seniority = false; /* Reset */ | |
} | |
/* Search this employee entry for the tester skill and print the result.*/ | |
find_tester_skill(employee_entry); | |
} | |
/* Confirm that the loop did not terminate abnormally. */ | |
if (return_code == HWTJ_OK) { | |
success = true; | |
} | |
} else { | |
display_error("Unable to retrieve number of entries from employee " | |
"array"); | |
} | |
return success; | |
} | |
/* | |
* Method: store_and_print_employee_information | |
* | |
* Retrieves the name, employee_id, years_of_service, and full_time entries | |
* from the specified employee entry. The data is stored in an employee struct | |
* and displayed. | |
*/ | |
void store_and_print_employee_info(HWTJ_HANDLE_TYPE employee_entry, | |
employee_record employee) { | |
/* Use "find_*" to retrieve each field from the employee entry.*/ | |
char *employee_name = find_string(employee_entry, "name"); | |
char *employee_id = find_string(employee_entry, "employee_id"); | |
char *years_of_service = find_number(employee_entry, "years_of_service"); | |
bool is_full_time = find_boolean(employee_entry, "full_time"); | |
/* Store the employee data in an employee struct. */ | |
employee.name = employee_name; | |
employee.id = employee_id; | |
employee.years_of_service = years_of_service; | |
employee.is_full_time = is_full_time; | |
/* Print the employee record. */ | |
print_employee_record(&employee); | |
} | |
/* | |
* Method: find_tester_skill | |
* | |
* Determines whether the specified employee has the "tester" skill. | |
* Each employee has provided different types of contact information. We want | |
* to know all of the employees contact information so we use traverse_object | |
* to "auto-discover" the data. The search services may not work in this case | |
* because the data is unpredictable. | |
*/ | |
void find_tester_skill(HWTJ_HANDLE_TYPE employee_entry) { | |
char *required_skill = "tester"; | |
/* Locate the "skills" array for this employee. */ | |
HWTJ_HANDLE_TYPE skills_array = find_array(employee_entry, "skills"); | |
HWTJ_HANDLE_TYPE contact_info = NULL; | |
if (has_skill(skills_array, required_skill)) { | |
printf("ALERT! Employee has experience as a %s. ", required_skill); | |
printf("Printing Contact Information.\n"); | |
/* Retrieve the contact info object. */ | |
contact_info = find_object(employee_entry, "contact"); | |
/* Traverse the contact info object and print out each name:value pair. */ | |
traverse_object(contact_info, 0); | |
} | |
} | |
/* | |
* Method: do_cleanup | |
* | |
* Performs cleanup by freeing memory used by the parser and invalidating the | |
* parser handle. | |
* | |
* Services Used: | |
* | |
* HWTJTERM: Terminates a parser instance and frees the storage allocated | |
* by the parse services. | |
* | |
* USAGE: The third parameter to hwtjterm is used to specify the | |
* behavior of terminate if the parser is determined to be stuck | |
* in an "in-use" state. IBM recommends using the HWTJ_NOFORCE | |
* option in most cases. Because our sample is not multi-threaded, | |
* the risk of the parser getting stuck in an "in-use" state is | |
* low. Therefore, we provide a value of HWTJ_NOFORCE for the | |
* force option. | |
* | |
* NOTE: Consider enhancing this sample to postpone the call to the | |
* terminate service when a prior service call resulted in a return code of | |
* HWTJ_UNEXPECTED_ERROR. This will allow appropriate action to be taken to | |
* dump the work area storage for subsequent analysis by the IBM support | |
* center. Once the dump has been taken, terminate can be issued to free the | |
* storage from the user's address space. | |
*/ | |
bool do_cleanup() { | |
/* Declare a variable to hold the return value. */ | |
bool success = false; | |
/* | |
* On the first attempt, try to terminate with the force option disabled. | |
* This is the IBM recommended value for the force option. If the parser is | |
* in an inuse state, further cleanup processing is done in the following | |
* EVALUATE statement. A parser can be in an INUSE state if a prior service | |
* call encountered an unexpected error that caused it to exit abnormally, or | |
* if the parser-handle is used in a multi-threaded application. | |
*/ | |
if (return_code != HWTJ_PARSERHANDLE_INUSE) { | |
/* Perform cleanup. */ | |
hwtjterm(&return_code, parser_instance, HWTJ_NOFORCE, &diag_area); | |
} | |
/* Determine whether further cleanup processing is necessary. */ | |
switch (return_code) { | |
case HWTJ_OK: | |
printf("SUCCESS: Parser work area freed.\n"); | |
success = true; | |
break; | |
case HWTJ_PARSERHANDLE_INUSE: | |
display_error("Unable to perform cleanup.\n " | |
"Retrying cleanup with HWTJ_FORCE option enabled."); | |
/* Attempt to force cleanup. Use with caution as recommended in the | |
* parser documentation | |
*/ | |
hwtjterm(&return_code, parser_instance, HWTJ_FORCE, &diag_area); | |
/* Check if cleanup was successful. */ | |
if (return_code == HWTJ_OK) { | |
printf("SUCCESS: Parser work area freed using force option.\n"); | |
success = true; | |
} else { | |
display_error("Unable to perform cleanup with HWTJ_FORCE option " | |
"enabled.\nCould not free parser work area."); | |
} | |
break; | |
default: | |
display_error("Unable to perform cleanup.\n " | |
"Could not free parser work area."); | |
} | |
/* Release the memory allocated for the serialized JSON data. */ | |
if (modified_json_string) { | |
free(modified_json_string); | |
modified_json_string = NULL; | |
} | |
return success; | |
} | |
/* | |
* Method: find_string | |
* | |
* Searches the specified JSON object for a name:value pair where the name | |
* matches the specified search string and the value type is string. This is a | |
* convenience method that can be used when the structure of the JSON data is | |
* known beforehand. | |
* | |
* Input: - A handle of type object or array. | |
* - A string used as the search parameter. | |
* | |
* Output: If a match is found, the string value of the name:value pair is | |
* returned. | |
*/ | |
char *find_string(HWTJ_HANDLE_TYPE object, char *search_string) { | |
return (char *) find_value(object, search_string, HWTJ_STRING_TYPE); | |
} | |
/* | |
* Method: find_number | |
* | |
* Searches the specified JSON object for a name:value pair where the name | |
* matches the specified search string and the value type is number. This is a | |
* convenience method that can be used when the structure of the JSON data is | |
* known beforehand. | |
* | |
* Input: - A handle of type object or array. | |
* - A string used as the search parameter. | |
* | |
* Output: If a match is found, the number value of the name:value pair is | |
* returned. | |
* | |
* Notes: The JSON spec (RFC 4627) allows numbers to be specified in several | |
* different formats (e.g. 3.00, 3.0e+2). This sample program makes no effort | |
* to convert JSON numeric data into the appropriate C data type -- all numeric | |
* data is treated as string data. | |
*/ | |
char *find_number(HWTJ_HANDLE_TYPE object, char *search_string) { | |
return (char *) find_value(object, search_string, HWTJ_NUMBER_TYPE); | |
} | |
/* | |
* Method: find_boolean | |
* | |
* Searches the specified JSON object for a name:value pair where the name | |
* matches the specified search string and the value type is boolean. This is a | |
* convenience method that can be used when the structure of the JSON data is | |
* known beforehand. | |
* | |
* Input: - A handle of type object or array. | |
* - A string used as the search parameter. | |
* | |
* Output: If a match is found, the boolean value of the name:value pair is | |
* returned. | |
*/ | |
bool find_boolean(HWTJ_HANDLE_TYPE object, char *search_string) { | |
HWTJ_BOOLEANVALUE_TYPE *bool_val_addr = NULL; | |
bool_val_addr = (HWTJ_BOOLEANVALUE_TYPE *) | |
find_value(object, search_string, HWTJ_BOOLEAN_TYPE); | |
return (*bool_val_addr == HWTJ_TRUE) ? true : false; | |
} | |
/* | |
* Method: find_array | |
* | |
* Searches the specified JSON object for a name:value pair where the name | |
* matches the specified search string and the value type is array. This is a | |
* convenience method that can be used when the structure of the JSON data is | |
* known beforehand. | |
* | |
* Input: - A handle of type object or array. | |
* - A string used as the search parameter. | |
* | |
* Output: If a match is found, a handle to the array value of the name:value | |
* pair is returned. | |
*/ | |
HWTJ_HANDLE_TYPE find_array(HWTJ_HANDLE_TYPE object, char *search_string) { | |
HWTJ_HANDLE_TYPE *array_handle_addr = | |
(HWTJ_HANDLE_TYPE *) find_value(object, search_string, HWTJ_ARRAY_TYPE); | |
return *array_handle_addr; | |
} | |
/* | |
* Method: find_object | |
* | |
* Searches the specified JSON object for a name:value pair where the name | |
* matches the specified search string and the value type is object. This is a | |
* convenience method that can be used when the structure of the JSON data is | |
* known beforehand. | |
* | |
* Input: - A handle of type object or array. | |
* - A string used as the search parameter. | |
* | |
* Output: If a match is found, a handle to the object value of the name:value | |
* pair is returned. | |
*/ | |
HWTJ_HANDLE_TYPE find_object(HWTJ_HANDLE_TYPE object, char *search_string) { | |
HWTJ_HANDLE_TYPE *obj_handle_addr = (HWTJ_HANDLE_TYPE *) | |
find_value(object, search_string, HWTJ_OBJECT_TYPE); | |
return *obj_handle_addr; | |
} | |
/* | |
* Method: find_value | |
* | |
* Searches the specified object for a name:value pair whose name matches the | |
* the specified search string. This is a utility method used by the "find" | |
* routines to easily search and retrieve a value from an object when the name | |
* and value type is known. | |
* | |
* Input: - A handle of type object or array. | |
* - A string used as a search parameter. | |
* - A JSON type as defined in the IBM-provided C interface definition | |
* file. | |
* | |
* Output: A pointer to the value is returned. | |
* | |
* Services Used: | |
* HWTJGJST: Gets the JSON type associated with a specified object or entry | |
* handle. | |
* HWTJSRCH: Finds a particular name string within the JSON text. | |
* | |
*/ | |
void *find_value(HWTJ_HANDLE_TYPE object_to_search, char *name, | |
HWTJ_JTYPE_TYPE expected_value_type) { | |
/* Declare a handle to store a pointer to value. */ | |
void *value_addr = NULL; | |
/* Declare a variable to hold the value if a match is found. */ | |
HWTJ_HANDLE_TYPE value_handle = 0; | |
/* Search the specified object for the specified name. */ | |
hwtjsrch(&return_code, | |
parser_instance, | |
HWTJ_SEARCHTYPE_OBJECT, /* limit the search scope */ | |
(char *)&name, /* search string address */ | |
strlen(name), /* search string length */ | |
object_to_search, /* handle of object to search */ | |
0, /* starting point of the search */ | |
&value_handle, /* search result handle (output) */ | |
&diag_area); | |
/* Check that the search found a result. */ | |
if (return_code == HWTJ_OK) { | |
/* Declare a variable to hold the entry type returned by hwtjgjst. */ | |
HWTJ_JTYPE_TYPE entry_type; | |
/* Get the object's type. */ | |
hwtjgjst(&return_code, | |
parser_instance, | |
value_handle, /* handle to the value whose type to check (input) */ | |
&entry_type, /* value type constant returned by hwtjgjst (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Verify that the returned handle has the expected type. */ | |
if (entry_type == expected_value_type) { | |
value_addr = do_get_value(value_handle, entry_type); | |
} else { | |
printf("Error occurred while searching for %s\nThe name was found, " | |
"but the value was not of the expected type.\n", name); | |
printf("Expected type: %d\nActual type: %d\n", | |
expected_value_type, entry_type); | |
} | |
} else { | |
display_error("ERROR: Unable to retrieve JSON type."); | |
} | |
} else { | |
printf("ERROR: Search failed for name \"%s\". " | |
"Name was not found in the specified object.\n", name); | |
} | |
/* | |
* At this point, if the search did not return a match, or the | |
* expected type did not match the actual type, the value_addr | |
* output parm is still set to NULL. Otherwise, value_addr | |
* points to the appropriate address. The caller is responsible | |
* for casting the void pointer to the appropriate pointer type. | |
*/ | |
return value_addr; | |
} | |
/* | |
* Method: do_get_value | |
* | |
* Retrieves the specified value by calling the appropriate service using the | |
* value of the specified HWTJ_JTYPE_TYPE. | |
* | |
* Input: - A value handle. | |
* - A valid entry type as defined in the IBM-provided C interface | |
* definition file. | |
* | |
* Services Used: | |
* HWTJGVAL: Retrieves the value of string or number entry. | |
* HWTJGBOV: Retrieves the value of a boolean entry. | |
*/ | |
void *do_get_value(HWTJ_HANDLE_TYPE value_handle, | |
HWTJ_JTYPE_TYPE entry_type) { | |
/* Declare a variable to hold the pointer to the copy. */ | |
void *value_addr = NULL; | |
/* | |
* The following checks determine the value's type and set | |
* the value_addr output parm appropriately. | |
* | |
* In the case of a string or number type, the source text is | |
* copied into a new buffer, and the value_addr output parm is | |
* set to the address of this buffer. | |
* | |
* In the case of a boolean value type, the HWTJ_TRUE/HWTJ_FALSE | |
* value is converted to a bool type and the value_addr output | |
* parm is set to the address of the bool value. | |
* | |
* In the case of an object or array type, the value_addr output | |
* parm is set to the address of the object or array handle. | |
*/ | |
/* Determine the value type. */ | |
if ((entry_type == HWTJ_STRING_TYPE) || | |
(entry_type == HWTJ_NUMBER_TYPE)) { | |
/* Declare a variable to store the length returned by hwtjgval. */ | |
int value_length = 0; | |
/* Declare a variable to store the address returned by hwtjgval. */ | |
int string_value_addr = 0; | |
/* Issue hwtjgval to get the address and length of the string. */ | |
hwtjgval(&return_code, | |
parser_instance, | |
value_handle, /* handle to a value (input) */ | |
&string_value_addr, /* value address (output) */ | |
&value_length, /* returned value length (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Allocate memory to store a copy of the string + null terminator. */ | |
value_addr = malloc(value_length + 1); | |
/* Copy the JSON source text to the local variable. */ | |
strncpy((char *)value_addr, (char *)string_value_addr, value_length); | |
/* Append the null-terminator. */ | |
strcat((char *)value_addr, "\0"); | |
if (entry_type == HWTJ_NUMBER_TYPE) { | |
int num_value = 0; | |
int *num_value_ptr = &num_value; | |
HWTJ_VALDESCRIPTOR_TYPE value_desc = 0; | |
/* Issue HWTJGNUV to get the binary representation | |
* of the number value. | |
*/ | |
hwtjgnuv(&return_code, | |
parser_instance, | |
value_handle, | |
&num_value_ptr, /* pointer to output buffer */ | |
sizeof(num_value), /* size of output buffer */ | |
&value_desc, /* indicates 2's comp or BFP */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Verify that the converted value is an integer type. | |
This check can be skipped if you are confident that | |
the numeric data will only be specified as integer | |
data. | |
*/ | |
if (value_desc == HWTJ_INTEGER_VALUE && num_value >= 10) { | |
employee_with_seniority = true; | |
} | |
} else { | |
display_error("An error occurred in Do_Get_Value. " | |
"HWTJGNUV failed."); | |
} | |
} | |
} else { | |
display_error("Unable to retrieve value."); | |
} | |
} else if (entry_type == HWTJ_BOOLEAN_TYPE) { | |
/* Declare a variable to store the value returned by hwtjgbov. */ | |
HWTJ_BOOLEANVALUE_TYPE hwtj_boolean; | |
/* Retrieve the value and store it in a local variable. */ | |
hwtjgbov(&return_code, | |
parser_instance, | |
value_handle, /* handle to the value (input) */ | |
&hwtj_boolean, /* boolean value returned by hwtjgbov (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Store the address of the boolean value in our return value. */ | |
value_addr = (void*)&hwtj_boolean; | |
} else { | |
display_error("Unable to retrieve boolean value."); | |
} | |
} else if ((entry_type == HWTJ_ARRAY_TYPE) || | |
(entry_type == HWTJ_OBJECT_TYPE)) { | |
/* Store the address of the handle in our return variable. */ | |
value_addr = &value_handle; | |
} | |
return value_addr; | |
} | |
/* | |
* Method: traverse_json_entry | |
* | |
* Performs a recursive, depth-first traversal of the specified JSON entry. | |
* When the provided handle is an array or object type, it is passed to the | |
* appropriate traversal subroutine (traverse_object or traverse_array). When | |
* the provided handle is a primitive type (i.e. string, number, bool, | |
* or null type) the value is retrieved and displayed. | |
* | |
* Usage: This subroutine demonstrates a common way for "auto-discovering" | |
* JSON data. It is especially useful when the data is unpredictable, | |
* i.e. not guaranteed to contain particular name:value pairs. The | |
* auto-discovery services contrast with the search services, which are | |
* based on the principle that the JSON data is predictable and is | |
* guaranteed to contain specific name:value pairs. As always, | |
* there is no one-size fits all solution and the traversal method that | |
* makes the most sense will depend on the structure of the JSON data. | |
* | |
* | |
* Input: | |
* - A value handle. | |
* - An integer used to format the printed JSON data. | |
* | |
* Output: None | |
* | |
* Services Used: | |
* | |
* HWTJGJST: Get the JSON type associated with a specified object or entry | |
* handle. | |
* | |
*/ | |
void traverse_json_entry(HWTJ_HANDLE_TYPE entry_handle, int indent_level) { | |
/* Declare a variable to hold the value returned by hwtjgjst. */ | |
HWTJ_JTYPE_TYPE entry_type; | |
/* In order to properly traverse this JSON entry, we first must determine the | |
* entry type. | |
*/ | |
hwtjgjst(&return_code, | |
parser_instance, | |
entry_handle, /* handle to a JSON entry (input) */ | |
&entry_type, /* value type of the handle (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* | |
* Switch on the object's type. For primitive types (string, number, | |
* boolean, null) the value is displayed. For object and array types, the | |
* handle is passed to the appropriate traversal method. | |
*/ | |
switch (entry_type) { | |
case HWTJ_OBJECT_TYPE: { | |
traverse_object(entry_handle, indent_level + 2); | |
break; | |
} | |
case HWTJ_ARRAY_TYPE: { | |
traverse_array(entry_handle, indent_level + 2); | |
break; | |
} | |
case HWTJ_STRING_TYPE: { | |
/* Declare a pointer to store the string value. */ | |
char *string_value_addr = NULL; | |
/* Get the string value. */ | |
string_value_addr = | |
(char *)do_get_value(entry_handle, HWTJ_STRING_TYPE); | |
if (string_value_addr != NULL) { | |
/* | |
* Display the value. The indent value is used to left-pad the | |
* printed string with spaces. | |
*/ | |
printf("%*s\n", indent_level + strlen(string_value_addr), | |
string_value_addr); | |
} | |
break; | |
} | |
case HWTJ_NUMBER_TYPE: { | |
/* Declare a pointer to store the value address. */ | |
char *num_value_addr = NULL; | |
/* Get the value. */ | |
num_value_addr = (char *)do_get_value(entry_handle, HWTJ_NUMBER_TYPE); | |
if (num_value_addr != NULL) { | |
/* | |
* Display the value. The indent value is used to left-pad the | |
* printed string with spaces. | |
*/ | |
printf("%*s\n", indent_level + strlen(num_value_addr), | |
num_value_addr); | |
} | |
break; | |
} | |
case HWTJ_BOOLEAN_TYPE: { | |
/* Declare a pointer to store the value address. */ | |
HWTJ_BOOLEANVALUE_TYPE *bool_value_addr = NULL; | |
/* Get a pointer to the boolean value. */ | |
bool_value_addr = (HWTJ_BOOLEANVALUE_TYPE*) | |
do_get_value(entry_handle, HWTJ_BOOLEAN_TYPE); | |
if (bool_value_addr != NULL) { /* Display the value. */ | |
printf("%*d\n", indent_level + 4, *bool_value_addr); | |
} | |
break; | |
} | |
case HWTJ_NULL_TYPE: { | |
printf("null\n"); | |
break; | |
} | |
default: { | |
/* If we've reached this point there is a problem. */ | |
printf("unknown type\n"); | |
break; | |
} | |
}/* end switch. */ | |
} else { | |
display_error("Unable to retrieve JSON type."); | |
} | |
} | |
/* | |
* Method: traverse_object | |
* | |
* Traverses the specified object. Traversal is accomplished by retrieving each | |
* name:value pair contained within the object and passing it to | |
* traverse_json_entry. | |
* | |
* Services Used: | |
* | |
* HWTJGNUE: Gets the number of entries in an object. | |
* HWTJGOEN: Retrieves a name:value pair from an object. | |
* | |
* Usage: These services can be used together to loop thru each name:value | |
* pair contained within an object. The value returned by hwtjgnue is | |
* used as a loop control variable, and hwtjgoen is used to retrieve | |
* each name:value pair. | |
*/ | |
void traverse_object(HWTJ_HANDLE_TYPE object_handle, int indent_level) { | |
/* Declare a variable to hold the value returned by hwtjgnue. */ | |
int num_of_entries = 0; | |
/* In order to traverse this object, first get the number of name:value pairs | |
* in the object. The HWTJGNUE service can be used in conjunction with | |
* HWTJGOEN to visit each entry in an object.*/ | |
hwtjgnue(&return_code, | |
parser_instance, | |
object_handle, /* object handle (input) */ | |
&num_of_entries, /* number of name:value pairs (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Declare a variable to hold the current object entry for processing. */ | |
HWTJ_HANDLE_TYPE entry_value_handle; | |
/* Declare a buffer to hold the name returned by hwtjgoen. */ | |
char *entry_name_buffer = (char *) malloc(MIN_NAME_LENGTH); | |
/* Declare a variable to hold the actual length returned by hwtjgoen. */ | |
int actual_name_length = 0; | |
/* Use the get object entry service to retrieve the next name:value pair. | |
* The HWTJGOEN service requires an output buffer to store the name portion | |
* of the name:value pair. If the buffer is not large enough to store the | |
* name, a warning code is returned and the actualNameLen output parameter | |
* contains the actual length of the name string. The HWTJGOEN service can | |
* then be re-issued with a buffer of sufficient size to store the name. | |
*/ | |
for (int i = 0; i < num_of_entries && return_code == HWTJ_OK; i += 1) { | |
/* Get the next name:value pair. */ | |
hwtjgoen(&return_code, | |
parser_instance, | |
object_handle, /* handle to the object (input) */ | |
i, /* index into the object (input) */ | |
(char *)&entry_name_buffer, /* entry name buffer (output) */ | |
MIN_NAME_LENGTH, /* length of the provided buffer (input) */ | |
&entry_value_handle, /* handle to the value portion (output) */ | |
&actual_name_length, /* actual length of the name (output) */ | |
&diag_area); | |
/* | |
* Check if the provided buffer was large enough to hold the name. | |
* If not, increase the buffer size to the amount returned by hwtjgoen. | |
*/ | |
if (return_code == HWTJ_BUFFER_TOO_SMALL) { | |
/* Use the returned length to increase the buffer size. */ | |
entry_name_buffer = (char *)realloc(entry_name_buffer, | |
actual_name_length * sizeof(char)); | |
/* Confirm that the allocation was successful. */ | |
if (entry_name_buffer != NULL) { | |
/* Re-issue hwtjgoen to retrieve the complete object entry name. */ | |
hwtjgoen(&return_code, | |
parser_instance, | |
object_handle, | |
i, | |
(char *)&entry_name_buffer, | |
actual_name_length, | |
&entry_value_handle, | |
&actual_name_length, | |
&diag_area); | |
if (return_code != HWTJ_OK) { | |
display_error("Unable to retrieve object entry."); | |
} | |
} | |
} | |
if (return_code == HWTJ_OK) { | |
/* Print the entry name (left-padded with spaces). */ | |
printf("%*s:\n", indent_level + actual_name_length, entry_name_buffer); | |
/* Traverse the value portion of this object. */ | |
traverse_json_entry(entry_value_handle, indent_level + 2); | |
} else { | |
display_error("Unable to retrieve object entry."); | |
} | |
} | |
} else { | |
display_error("Unable to retrieve the number of entries from object."); | |
} | |
} | |
/* | |
* Method: traverse_array | |
* | |
* Traverses the specified array. Traversal is accomplished by retrieving each | |
* array entry and passing it to traverse_json_entry. | |
* | |
* Services Used: | |
* | |
* HWTJGNUE: Gets the number of entries in an array or object. | |
* HWTJGAEN: Retrieves a handle to an array entry using an index value. | |
* | |
* Usage: These services are normally combined in order to loop over each | |
* entry in an array. The value returned by hwtjgnue is used as a | |
* loop control variable, and hwtjgaen is used to retrieve each entry | |
* contained within the array. | |
* | |
*/ | |
void traverse_array(HWTJ_HANDLE_TYPE array_handle, int indent_level) { | |
/* Declare a variable to hold the value returned from hwtjgnue. */ | |
int num_of_entries = 0; | |
/* In order to traverse this array, first determine how many values are | |
* contained in the array using the HWTJGNUE service. The HWTJGNUE service | |
* can be used in conjunction with HWTJGAEN to visit each value in an array. | |
*/ | |
hwtjgnue(&return_code, | |
parser_instance, | |
array_handle, /* handle to the array (input) */ | |
&num_of_entries, /* number of array entries (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Declare a variable to hold the current array entry. */ | |
HWTJ_HANDLE_TYPE array_entry_handle; | |
/* Begin the loop to process each array entry. */ | |
for (int i = 0; i < num_of_entries && return_code == HWTJ_OK; i += 1) { | |
/* Get the next array entry. */ | |
hwtjgaen(&return_code, | |
parser_instance, | |
array_handle, /* handle to the array (input) */ | |
i, /* index into the array (input) */ | |
&array_entry_handle, /* handle to the i-th entry (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Traverse this array entry. */ | |
traverse_json_entry(array_entry_handle, indent_level + 2); | |
} else { | |
display_error("Unable to retrieve array entry."); | |
} | |
} | |
} else { | |
display_error("Unable to retrieve the number of entries from array."); | |
} | |
} | |
/* | |
* Method: has_skill | |
* | |
* Determines whether the specified array contains an entry whose value matches | |
* the specified search string. Returns true if the array contains an entry | |
* matching the specified skill, returns false otherwise. | |
* | |
* Input: - An array handle | |
* - A string used as a search parameter. | |
* | |
* Output: Returns true if the array contains a string value entry that matches | |
* the specified search string. | |
* | |
* Services Used: | |
* | |
* HWTJGJST: Get the JSON type associated with a specified object or entry | |
* handle. | |
* HWTJGNUE: Gets the number of entries in an array or object. | |
* HWTJGAEN: Retrieves a handle to an array entry using an index value. | |
* HWTJGVAL: Retrieves the value of string or number entry. | |
* | |
*/ | |
bool has_skill(HWTJ_HANDLE_TYPE skills_array, char *skill) { | |
/* A flag variable to indiciate whether a match was found. */ | |
bool is_match = false; | |
HWTJ_JTYPE_TYPE entry_type; | |
/* Issue hwtjgjst to retrieve the object's type. */ | |
hwtjgjst(&return_code, | |
parser_instance, | |
skills_array, /* handle to the value whose type to check (input) */ | |
&entry_type, /* value type constant returned by hwtjgjst (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Verify that the type is an array. */ | |
if (entry_type != HWTJ_ARRAY_TYPE) { | |
return false; | |
} | |
/* Next, verify that the user passed a search string. */ | |
if (skill && strlen(skill) <= 0) { | |
return false; | |
} | |
/* Retrieve each string value in the job roles array. */ | |
int num_of_skills = 0; | |
hwtjgnue(&return_code, | |
parser_instance, | |
skills_array, /* handle to an array or object (input) */ | |
&num_of_skills, /* number of entries in the array (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Get the string value of each entry. */ | |
for (int i=0; i < num_of_skills && !is_match && return_code == HWTJ_OK; | |
i+=1) { | |
/* A handle to hold the array entry returned by hwtjgaen. */ | |
HWTJ_HANDLE_TYPE skills_entry; | |
/* Get the next array entry. */ | |
hwtjgaen(&return_code, | |
parser_instance, | |
skills_array, /* handle to the array (input) */ | |
i, /* index into the array (input) */ | |
&skills_entry, /* handle to the i-th entry (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* We are expecting each entry to be a string, but we will check | |
the type to be sure, because null values are valid as well. */ | |
hwtjgjst(&return_code, | |
parser_instance, | |
skills_entry, /* value handle (input) */ | |
&entry_type, /* value type constant (output) */ | |
&diag_area); | |
/* Again, we are expecting string types, but just to be sure. */ | |
if (return_code == HWTJ_OK && entry_type == HWTJ_STRING_TYPE) { | |
/* Get the string value. */ | |
int entry_value_length = 0; | |
int *entry_value_addr = NULL; | |
/* Issue hwtjgval to get the address and length of the string. */ | |
hwtjgval(&return_code, | |
parser_instance, | |
skills_entry, /* value handle (input) */ | |
(int*)&entry_value_addr, /* output buffer address */ | |
&entry_value_length, /* actual value length (output) */ | |
&diag_area); | |
if (return_code == HWTJ_OK) { | |
/* Does this job role match the specified job role? */ | |
is_match = | |
!strncmp(skill, (char *)entry_value_addr, strlen(skill)); | |
} else { | |
display_error("Unable to retrieve value from array."); | |
} | |
} | |
} else { | |
display_error("Unable to retrieve array entry."); | |
} | |
} /* end for loop */ | |
} else { | |
display_error("Unable to retrieve number of entries in array."); | |
} | |
} else { | |
display_error("Unable to retrieve JSON type."); | |
} | |
/* Return the result of the search to the caller. */ | |
return is_match; | |
} | |
/* | |
* Method: to_json_string | |
* | |
* Returns a pointer to the serialized version of the JSON data. If an an error | |
* occurs during serialization a NULL pointer is returned. | |
* | |
* Services Used: | |
* | |
* HWTJSERI: Generates a JSON text string representing the data . | |
* | |
* USAGE: The serialize service generates a JSON-formatted text string using | |
* the data available through the provided parser handle. It is | |
* especially useful after using the create services to modify | |
* or insert additional JSON data. | |
*/ | |
char *to_json_string() { | |
/* Declare a variable to hold the returned length. */ | |
int actual_length = 0; | |
/* Specify the buffer size input variable. */ | |
int buffer_size = MAX_BUFFER_SIZE; | |
/* Allocate memory to store the serialized text. */ | |
char *serialized_text_buffer = (char *)malloc(buffer_size); | |
/* Generate a JSON formatted text string using the current state of the JSON | |
* data. The JSON text returned by the serialize service (HWTJSERI) will | |
* include all modifications and insertions done through the create service | |
* (HWTJCREN). If the provided output buffer is not large enough to contain | |
* the entire serialized text string, a warning return code is returned and | |
* the actual-output-length parameter is set to to the actual size required | |
* by the buffer. This value can be used to increase the buffer size to the | |
* required amount. The HWTJSERI can be called again to obtain the complete | |
* serialized text string. | |
*/ | |
hwtjseri(&return_code, | |
parser_instance, | |
(char *)&serialized_text_buffer, /* buffer address (output) */ | |
buffer_size, /* output buffer size (input) */ | |
&actual_length, /* actual size of serialized JSON text (output) */ | |
&diag_area); | |
/* Determine whether serialize returned successfully. */ | |
if (return_code == HWTJ_OK) { | |
printf("SUCCESS: JSON data serialized.\n"); | |
} else if (return_code == HWTJ_BUFFER_TOO_SMALL) { | |
/* If the error occurred because the provided buffer was too small, | |
use the returned length to increase the buffer size. */ | |
serialized_text_buffer = (char *)realloc(serialized_text_buffer, | |
actual_length); | |
/* Confirm that the allocation was successful. */ | |
if (serialized_text_buffer != NULL) { | |
buffer_size = actual_length; | |
/* Re-issue hwtjseri to retrieve complete serialized text string. */ | |
hwtjseri(&return_code, | |
parser_instance, | |
(char *)&serialized_text_buffer, /* buffer address (input) */ | |
buffer_size, /* output buffer size (input) */ | |
&actual_length, /* actual size of serialized JSON text (output) */ | |
&diag_area); | |
if (return_code != HWTJ_OK) { | |
display_error("Unable to serialize JSON data."); | |
} | |
} | |
} else { | |
display_error("Unable to serialize JSON data."); | |
} | |
return serialized_text_buffer; | |
} | |
/* | |
* Method: display_error | |
* | |
* A helper method for displaying error diagnostic information. | |
*/ | |
void display_error(char *msg) { | |
printf("ERROR: %s\n", msg); | |
printf("Return Code: %d\n", return_code); | |
printf("Reason Code: %d\n", diag_area.ReasonCode); | |
printf("Reason Text: %s\n", diag_area.ReasonDesc); | |
} | |
/* | |
* Method: print_employee_record | |
* | |
* A helper method for displaying an employee record struct. | |
*/ | |
void print_employee_record(employee_record *rec) { | |
printf(" * * * EMPLOYEE ENTRY * * *\n"); | |
printf("NAME: %s\n", rec->name); | |
printf("ID: %s\n", rec->id); | |
printf("YEARS: %s\n", rec->years_of_service); | |
printf("FULLTIME: %s\n", rec->is_full_time ? "true" : "false"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment