Add various new JSON data format parsing functions

This commit is contained in:
Aaron Culliney 2016-02-13 14:02:35 -08:00
parent 5bb65e3fc3
commit 70afe71c82
2 changed files with 389 additions and 86 deletions

View File

@ -14,22 +14,89 @@
#define JSON_LENGTH 16
#define DEFAULT_NUMTOK 16
static int _json_createFromString(const char *jsonString, INOUT JSON_s *parsedData, ssize_t jsonLen) {
jsmnerr_t errCount = JSMN_ERROR_NOMEM;
do {
jsmn_parser parser = { 0 };
if (!parsedData) {
break;
}
parsedData->jsonString = strdup(jsonString);
parsedData->jsonTokens = NULL;
unsigned int numTokens = DEFAULT_NUMTOK;
do {
if (!parsedData->jsonTokens) {
parsedData->jsonTokens = CALLOC(numTokens, sizeof(jsmntok_t));
if (!parsedData->jsonTokens) {
ERRLOG("WHOA3 : %s", strerror(errno));
break;
}
} else {
//LOG("reallocating json tokens ...");
numTokens <<= 1;
jsmntok_t *newTokens = REALLOC(parsedData->jsonTokens, numTokens * sizeof(jsmntok_t));
if (!newTokens) {
ERRLOG("WHOA4 : %s", strerror(errno));
break;
}
memset(newTokens, '\0', numTokens * sizeof(jsmntok_t));
parsedData->jsonTokens = newTokens;
}
jsmn_init(&parser);
errCount = jsmn_parse(&parser, parsedData->jsonString, jsonLen, parsedData->jsonTokens, numTokens-(numTokens/2));
} while (errCount == JSMN_ERROR_NOMEM);
if (errCount == JSMN_ERROR_NOMEM) {
break;
}
if (errCount < 0) {
ERRLOG("%s", "OOPS error parsing JSON : ");
switch (errCount) {
case JSMN_ERROR_NOMEM:
assert(0 && "should not happen");
break;
case JSMN_ERROR_INVAL:
ERRLOG("%s", "Invalid character inside JSON string");
break;
case JSMN_ERROR_PART:
ERRLOG("%s", "String is not a complete JSON packet, moar bytes expected");
break;
default:
ERRLOG("UNKNOWN errCount : %d", errCount);
break;
}
break;
}
parsedData->numTokens = errCount;
} while (0);
if (errCount < 0) {
if (parsedData) {
json_destroy(parsedData);
}
}
return errCount;
}
int json_createFromFile(const char *filePath, INOUT JSON_s *parsedData) {
int fd = -1;
jsmnerr_t errCount = JSMN_ERROR_NOMEM;
ssize_t jsonIdx = 0;
ssize_t jsonLen = 0;
char *jsonString = NULL;
jsmntok_t *jsonTokens = NULL;
do {
if (!parsedData) {
break;
}
parsedData->jsonString = NULL;
parsedData->jsonTokens = NULL;
TEMP_FAILURE_RETRY(fd = open(filePath, O_RDONLY));
if (fd < 0) {
@ -74,81 +141,152 @@ int json_createFromFile(const char *filePath, INOUT JSON_s *parsedData) {
jsonLen = jsonIdx;
TEMP_FAILURE_RETRY(close(fd));
fd = 0;
fd = -1;
// now parse the string
jsmnerr_t errCount = _json_createFromString(jsonString, parsedData, jsonLen);
FREE(jsonString);
return errCount;
jsmn_parser parser = { 0 };
unsigned int numTokens = DEFAULT_NUMTOK;
do {
if (!jsonTokens) {
jsonTokens = CALLOC(numTokens, sizeof(jsmntok_t));
if (!jsonTokens) {
ERRLOG("WHOA3 : %s", strerror(errno));
break;
}
} else {
//LOG("reallocating json tokens ...");
numTokens <<= 1;
jsmntok_t *newTokens = REALLOC(jsonTokens, numTokens * sizeof(jsmntok_t));
if (!newTokens) {
ERRLOG("WHOA4 : %s", strerror(errno));
break;
}
memset(newTokens, '\0', numTokens * sizeof(jsmntok_t));
jsonTokens = newTokens;
}
jsmn_init(&parser);
errCount = jsmn_parse(&parser, jsonString, jsonLen, jsonTokens, numTokens-(numTokens/2));
} while (errCount == JSMN_ERROR_NOMEM);
if (errCount == JSMN_ERROR_NOMEM) {
break;
}
if (errCount < 0) {
ERRLOG("%s", "OOPS error parsing JSON : ");
switch (errCount) {
case JSMN_ERROR_NOMEM:
assert(0 && "should not happen");
break;
case JSMN_ERROR_INVAL:
ERRLOG("%s", "Invalid character inside JSON string");
break;
case JSMN_ERROR_PART:
ERRLOG("%s", "String is not a complete JSON packet, moar bytes expected");
break;
default:
ERRLOG("UNKNOWN errCount : %d", errCount);
break;
}
break;
}
parsedData->jsonString = jsonString;
parsedData->jsonTokens = jsonTokens;
} while (0);
if (fd > 0) {
TEMP_FAILURE_RETRY(close(fd));
fd = 0;
fd = -1;
}
if (errCount < 0) {
if (jsonString) {
FREE(jsonString);
}
if (jsonTokens) {
FREE(jsonTokens);
}
if (jsonString) {
FREE(jsonString);
}
return errCount;
return JSMN_ERROR_NOMEM;
}
int json_createFromString(const char *jsonString, INOUT JSON_s *parsedData) {
return _json_createFromString(jsonString, parsedData, strlen(jsonString));
}
static bool _json_mapGetStringValue(const JSON_s *map, const char *key, INOUT char **val, INOUT int *len) {
bool foundMatch = false;
do {
if (!val) {
break;
}
if (!len) {
break;
}
*val = NULL;
*len = -1;
int tokCount = map->numTokens;
if (tokCount < 0) {
break;
}
int idx=0;
const int keySize = strlen(key);
// should begin as map ...
if (map->jsonTokens[idx].type != JSMN_OBJECT) {
ERRLOG("Map JSON : must start with begin map token");
break;
}
++idx;
// Linear parse/search for key ...
for (int skip=0; idx<tokCount; idx+=skip) {
jsmntok_t keyTok = map->jsonTokens[idx];
assert(keyTok.parent == 0);
if (keyTok.type != JSMN_STRING) {
ERRLOG("Map JSON : expecting a string key at map position %d", idx);
break;
}
int start = keyTok.start;
int end = keyTok.end;
assert(end >= start && "bug");
const int size = end - start;
++idx;
jsmntok_t valTok = map->jsonTokens[idx];
if (size == keySize) {
foundMatch = (strncmp(key, &map->jsonString[start], size) == 0);
if (foundMatch) {
start = valTok.start;
end = valTok.end;
assert(end >= start && "bug");
*len = end - start;
*val = &map->jsonString[start];
break;
}
}
skip = valTok.skip;
}
} while (0);
return foundMatch;
}
bool json_mapCopyStringValue(const JSON_s *map, const char *key, INOUT char **val) {
int len = 0;
bool foundMatch = _json_mapGetStringValue(map, key, val, &len);
if (foundMatch) {
*val = len>0 ? strndup(*val, len) : strdup("");
}
return foundMatch;
}
bool json_mapParseLongValue(const JSON_s *map, const char *key, INOUT long *val, const int base) {
bool foundMatch = false;
do {
if (!val) {
break;
}
int len = 0;
char *str = NULL;
foundMatch = _json_mapGetStringValue(map, key, &str, &len);
if (foundMatch) {
char ch = str[len];
str[len] = '\0';
*val = strtol(str, NULL, base);
str[len] = ch;
}
} while (0);
return foundMatch;
}
bool json_mapParseFloatValue(const JSON_s *map, const char *key, INOUT float *val) {
bool foundMatch = false;
do {
if (!val) {
break;
}
int len = 0;
char *str = NULL;
foundMatch = _json_mapGetStringValue(map, key, &str, &len);
if (foundMatch) {
char ch = str[len];
str[len] = '\0';
*val = strtof(str, NULL);
str[len] = ch;
}
} while (0);
return foundMatch;
}
void json_destroy(JSON_s *parsedData) {
FREE(parsedData->jsonString);
STRDUP_FREE(parsedData->jsonString);
FREE(parsedData->jsonTokens);
}
@ -156,25 +294,12 @@ void json_destroy(JSON_s *parsedData) {
bool do_logging = true;
FILE *error_log = NULL;
// Runs jsmn against an arbitrary a test_file.json
int main(int argc, char **argv) {
static void _dumpJSON(JSON_s parsedData) {
error_log = stderr;
fprintf(stderr, "TOK COUNT:%d\n", parsedData.numTokens);
if (argc < 2) {
fprintf(stderr, "Please specify a JSON file to parse on CLI, e.g. : %s path/to/some_file.json\n", argv[0]);
exit(1);
}
JSON_s parsedData = { 0 };
int tokCount = json_createFromFile(argv[1], &parsedData);
if (tokCount < 0) {
return 1;
}
fprintf(stderr, "TOK COUNT:%d\n", tokCount);
for (int i=0; i<tokCount; i++) {
fprintf(stderr, "%s", "type:");
for (int i=0; i<parsedData.numTokens; i++) {
fprintf(stderr, "%6d %s", i, "type:");
switch(parsedData.jsonTokens[i].type) {
case JSMN_PRIMITIVE:
fprintf(stderr, "%s", "PRIMITIVE ");
@ -192,10 +317,11 @@ int main(int argc, char **argv) {
fprintf(stderr, "%s", "UNKNOWN ");
break;
}
int start = parsedData.jsonTokens[i].start;
int end = parsedData.jsonTokens[i].end;
fprintf(stderr, "start:%6d end:%6d size:%6d parent:%3d", start, end, parsedData.jsonTokens[i].size, parsedData.jsonTokens[i].parent);
if (parsedData.jsonTokens[i].type == JSMN_STRING) {
jsmntok_t tok = parsedData.jsonTokens[i];
int start = tok.start;
int end = tok.end;
fprintf(stderr, "start:%6d end:%6d size:%6d skipIdx:%6d parent:%3d", start, end, tok.size, tok.skip, tok.parent);
if (tok.type == JSMN_STRING || tok.type == JSMN_PRIMITIVE) {
char lastChar = parsedData.jsonString[end];
parsedData.jsonString[end] = '\0';
fprintf(stderr, " VALUE:%s", &parsedData.jsonString[start]);
@ -203,6 +329,156 @@ int main(int argc, char **argv) {
}
fprintf(stderr, "%s", "\n");
}
}
int main(int argc, char **argv) {
error_log = stderr;
if (argc < 2) {
fprintf(stderr, "Please specify a JSON file to parse on CLI, e.g. : %s path/to/some_file.json\n", argv[0]);
exit(1);
}
JSON_s parsedData = { 0 };
int tokCount = json_createFromFile(argv[1], &parsedData);
if (tokCount < 0) {
return 1;
}
fprintf(stderr, "-------------------------------------------------------------------------------\n");
fprintf(stderr, "DUMPING FILE %s ...\n", argv[1]);
fprintf(stderr, "-------------------------------------------------------------------------------\n");
fprintf(stderr, "\n");
_dumpJSON(parsedData);
json_destroy(&parsedData);
fprintf(stderr, "\n");
fprintf(stderr, "-------------------------------------------------------------------------------\n");
fprintf(stderr, "NOW TESTING MAP FUNCTIONS...\n");
fprintf(stderr, "-------------------------------------------------------------------------------\n");
fprintf(stderr, "\n");
const char *testMapStr0 =
" { "
" \"key0\" : \"a value zero\", "
" \"key1\" : \" \", "
" \"key2\" : { \n"
"} , "
" \"key3\" : { \n"
" \"subkey0\" : \"subval0\", "
" \"subkey1\" : { \"moar\" : \"recursion\" } , "
" \"subkey2\" : \"line0 \n"
" line1 "
" line2\" \n"
"}, "
" \"key4\" : [ \"Q\", \"W\", \"E\", "
" \"R\", \"T\", \"Y\", \"U\", \"I\", "
" \"O\", \"P\", { \"x\" : [ 22, 4, \"ab\" ] } ],"
" \"intKey0\" : 42 , "
" \"key5\" : \"\" , "
" \"intKey1\" : -101, "
" \"intKey2\" : -000000000, "
" \"intKey3\" : 0x2400, "
" \"intKey4\" : 10111111, "
" \"floatKey0\" : 0.0 , "
" \"floatKey1\" : -.0001220703125 ,"
" \"floatKey2\" : 3.1415928 , "
" \"floatKey3\" : -3.1e2 "
" } "
;
tokCount = json_createFromString(testMapStr0, &parsedData);
if (tokCount < 0) {
return 1;
}
_dumpJSON(parsedData);
long lVal;
float fVal;
char *val;
json_mapParseLongValue(&parsedData, "intKey2", &lVal, 10);
assert(lVal == 0);
json_mapCopyStringValue(&parsedData, "key0", &val);
assert(strcmp(val, "a value zero") == 0);
STRDUP_FREE(val);
json_mapCopyStringValue(&parsedData, "key1", &val);
assert(strcmp(val, " \t ") == 0);
STRDUP_FREE(val);
json_mapCopyStringValue(&parsedData, "key2", &val);
assert(strcmp(val, "{ \t \n}") == 0);
STRDUP_FREE(val);
json_mapCopyStringValue(&parsedData, "key3", &val);
assert(strcmp(val, "{ \t \n \"subkey0\" : \"subval0\", \"subkey1\" : { \"moar\" : \"recursion\" } , \"subkey2\" : \"line0 \n \tline1 \tline2\" \n}") == 0);
{
JSON_s parsedSubData = { 0 };
int tokSubCount = json_createFromString(val, &parsedSubData);
if (tokSubCount < 0) {
return 1;
}
char *subval;
json_mapCopyStringValue(&parsedSubData, "subkey0", &subval);
assert(strcmp(subval, "subval0") == 0);
STRDUP_FREE(subval);
json_mapCopyStringValue(&parsedSubData, "subkey1", &subval);
assert(strcmp(subval, "{ \"moar\" : \"recursion\" }") == 0);
STRDUP_FREE(subval);
json_mapCopyStringValue(&parsedSubData, "subkey2", &subval);
assert(strcmp(subval, "line0 \n \tline1 \tline2") == 0);
STRDUP_FREE(subval);
}
STRDUP_FREE(val);
json_mapCopyStringValue(&parsedData, "key4", &val);
assert(strcmp(val, "[ \"Q\", \"W\", \"E\", \"R\", \"T\", \"Y\", \"U\", \"I\", \"O\", \"P\", { \"x\" : [ 22, 4, \"ab\" ] } ]") == 0);
// TODO : subarray checks
STRDUP_FREE(val);
json_mapCopyStringValue(&parsedData, "key5", &val);
assert(strcmp(val, "") == 0);
STRDUP_FREE(val);
json_mapParseLongValue(&parsedData, "intKey0", &lVal, 10);
assert(lVal == 42);
json_mapParseLongValue(&parsedData, "intKey1", &lVal, 10);
assert(lVal == -101);
json_mapParseLongValue(&parsedData, "intKey3", &lVal, 16);
assert(lVal == 0x2400);
json_mapParseLongValue(&parsedData, "intKey4", &lVal, 2);
assert(lVal == 191);
json_mapParseLongValue(&parsedData, "intKey4", &lVal, 10);
assert(lVal == 10111111);
json_mapParseFloatValue(&parsedData, "floatKey0", &fVal);
assert(fVal == 0.f);
json_mapParseFloatValue(&parsedData, "floatKey1", &fVal);
assert(fVal == -.0001220703125);
json_mapParseFloatValue(&parsedData, "floatKey2", &fVal);
assert((long)(fVal*10000000) == 31415928);
json_mapParseFloatValue(&parsedData, "floatKey3", &fVal);
assert((long)fVal == -310);
json_destroy(&parsedData);
fprintf(stderr, "...map functions passed\n");
return 0;
}

View File

@ -9,17 +9,44 @@
*
*/
#ifndef _JSON_PARSE_H_
#define _JSON_PARSE_H_ 1
#include "common.h"
#include "../externals/jsmn/jsmn.h"
typedef struct JSON_s {
char *jsonString;
int numTokens;
jsmntok_t *jsonTokens;
} JSON_s;
// parses string into tokens. returns positive token count or negative jsmnerr_t error code.
int json_createFromString(const char *jsonString, INOUT JSON_s *parsedData);
// parses file into string and tokens. returns positive token count or negative jsmnerr_t error code.
int json_createFromFile(const char *filePath, INOUT JSON_s *parsedData);
// ----------------------------------------------------------------------------
// map functions
// get string value for key in map JSON, returns true upon success and strdup()'d value in *val
bool json_mapCopyStringValue(const JSON_s *map, const char *key, INOUT char **val);
// get int value for key in map JSON, returns true upon success
bool json_mapParseLongValue(const JSON_s *dict, const char *key, INOUT long *val, const int base);
// get float value for key in map JSON, returns true upon success
bool json_mapParseFloatValue(const JSON_s *dict, const char *key, INOUT float *val);
// ----------------------------------------------------------------------------
// array functions
//bool json_arrayCopytStringValueAtIndex(const JSON_s *array, unsigned long index, INOUT const char **val);
//bool json_arrayParseIntValueAtIndex(const JSON_s *array, unsigned long index, INOUT const int *val);
//bool json_arrayParseFloatValueAtIndex(const JSON_s *array, unsigned long index, INOUT const float *val);
// destroys internal allocated memory (if any)
void json_destroy(JSON_s *parsedData);
#endif