#ifndef __LIB_JSON_H__ #define __LIB_JSON_H__ /** * @file libjson.h * @date 2018-06-11 * * A json library for handling a json data structure. * Inspired by Java's @c org.json . */ #ifdef __cplusplus extern "C" { #endif #include #include /*~ Data Structures ~*/ /** * The data type of a json value. */ typedef enum JSONType { object, /**< {} */ array, /**< [] */ boolean, /**< true/false */ number, /**< 3.14 */ string, /**< "" */ null /**< null */ } JSONType; typedef struct JSONElement JSONElement; typedef struct JSONPair JSONPair; /** * A json object. * {} */ typedef struct JSONObject { JSONPair* elements; /**< A sequence of key/value pairs. */ int numberOfElements; /**< How many key/value pairs the object has. */ } JSONObject; /** * A json array. * [] */ typedef struct JSONArray { JSONElement* elements; /**< A sequence of values within the array. */ int numberOfElements; /**< How many values the array has. */ } JSONArray; /** * A boolean. * true / false */ typedef enum bool { false, /**< False */ true /**< True */ } bool; /** * The value of a member contained in json. */ typedef struct JSONElement { JSONType type; /**< The data type of the value. */ JSONObject object; /**< The value, if the type is an object. {} */ JSONArray array; /**< The value, if the type is an array. [] */ bool boolean; /**< The value, if the type is a boolean. true/false */ long double number; /**< The value, if the type is a number (integer or double). 3.14 */ char* string; /**< The value, if the type is a string (not including quotes). "" */ } JSONElement; /** * A key/value pair within a json object. * "key":value */ typedef struct JSONPair { char* key; /**< The pair's key. **/ JSONElement value; /**< The pair's value. **/ } JSONPair; /*~ Interface ~*/ /*============================================================================= JSONObject {} =============================================================================*/ // -- Constructor -- JSONObject o_emptyJSONObject(void); // -- Destructor -- void o_destroyJSONObject(JSONObject* json); // -- To String -- char* o_JSONObjectToString(JSONObject json); // -- Parser -- JSONObject o_parseJSONObject(char* string); // -- Check -- bool o_has(JSONObject json, char* key); bool o_isJSONObject(JSONObject json, char* key); bool o_isJSONArray(JSONObject json, char* key); bool o_isBoolean(JSONObject json, char* key); bool o_isInt(JSONObject json, char* key); bool o_isDouble(JSONObject json, char* key); bool o_isString(JSONObject json, char* key); bool o_isNull(JSONObject json, char* key); // -- Accessors -- JSONObject o_getJSONObject(JSONObject json, char* key); JSONArray o_getJSONArray(JSONObject json, char* key); bool o_getBoolean(JSONObject json, char* key); long o_getInt(JSONObject json, char* key); long double o_getDouble(JSONObject json, char* key); char* o_getString(JSONObject json, char* key); JSONObject o_optJSONObject(JSONObject json, char* key, JSONObject dflt); JSONArray o_optJSONArray(JSONObject json, char* key, JSONArray dflt); bool o_optBoolean(JSONObject json, char* key, bool dflt); long o_optInt(JSONObject json, char* key, long dflt); long double o_optDouble(JSONObject json, char* key, long double dflt); char* o_optString(JSONObject json, char* key, char* dflt); // -- Mutators -- void o_setJSONObject(JSONObject* json, char* key, JSONObject set); void o_setJSONArray(JSONObject* json, char* key, JSONArray set); void o_setBoolean(JSONObject* json, char* key, bool set); void o_setInt(JSONObject* json, char* key, long set); void o_setDouble(JSONObject* json, char* key, long double set); void o_setString(JSONObject* json, char* key, char* set); void o_setNull(JSONObject* json, char* key); void o_remove(JSONObject* json, char* key); /*============================================================================= JSONArray [] =============================================================================*/ // -- Constructor -- JSONArray a_emptyJSONArray(void); // -- Destructor -- void a_destroyJSONArray(JSONArray* json); // -- To String -- char* a_JSONArrayToString(JSONArray json); // -- Parser -- JSONArray a_parseJSONArray(char* string); // -- Check -- bool a_isJSONObject(JSONArray json, int index); bool a_isJSONArray(JSONArray json, int index); bool a_isString(JSONArray json, int index); bool a_isInt(JSONArray json, int index); bool a_isDouble(JSONArray json, int index); bool a_isBoolean(JSONArray json, int index); bool a_isNull(JSONArray json, int index); // -- Accessors -- JSONObject a_getJSONObject(JSONArray json, int index); JSONArray a_getJSONArray(JSONArray json, int index); bool a_getBoolean(JSONArray json, int index); long a_getInt(JSONArray json, int index); long double a_getDouble(JSONArray json, int index); char* a_getString(JSONArray json, int index); JSONArray a_optJSONArray(JSONArray json, int index, JSONArray dflt); JSONArray a_optJSONArray(JSONArray json, int index, JSONArray dflt); bool a_optBoolean(JSONArray json, int index, bool dflt); long a_optInt(JSONArray json, int index, long dflt); long double a_optDouble(JSONArray json, int index, long double dflt); char* a_optString(JSONArray json, int index, char* dflt); // -- Mutators -- void a_setJSONObject(JSONArray* json, int index, JSONObject set); void a_setJSONArray(JSONArray* json, int index, JSONArray set); void a_setBoolean(JSONArray* json, int index, bool set); void a_setInt(JSONArray* json, int index, long set); void a_setDouble(JSONArray* json, int index, long double set); void a_setString(JSONArray* json, int index, char* set); void a_setNull(JSONArray* json, int index); void a_remove(JSONArray* json, int index); /*~ Implementation ~*/ // -- Helper functions -- static char* libjson_emptyString(int size); static char* libjson_extendString(char* str, int additionalSize); static char* libjson_extractString(char* str); static char* libjson_JSONElementToString(JSONElement element); static char* libjson_JSONPairToString(JSONPair pair); static char* libjson_extractRaw(char* str, char open, char close); static char* libjson_strcpy(char* str); static bool libjson_isdigit(char c); static bool libjson_isspace(char c); static bool libjson_strcmp(char* s1, char* s2); static long double libjson_floor(long double d); static int libjson_strlen(char* str); static JSONElement libjson_emptyJSONElement(void); static JSONElement o_getJSONElement(JSONObject json, char* key); static JSONElement a_getJSONElement(JSONArray json, int index); static void o_setJSONElement(JSONObject* json, char* key, JSONElement set); static void a_setJSONElement(JSONArray* json, int index, JSONElement set); static void libjson_destroyJSONElement(JSONElement* element); static void libjson_destroyJSONPair(JSONPair* pair); static void libjson_deallocate(void** p); #define libjson_dealloc(p) libjson_deallocate((void**)&p) /*============================================================================= JSONObject {} =============================================================================*/ // -- Constructor -- /** * Create an empty json object. * * @return An empty json object. */ JSONObject o_emptyJSONObject(void) { JSONObject empty; empty.numberOfElements = 0; empty.elements = NULL; return empty; } // -- Destructor -- /** * Free all heap memory used by a json object. * * @param json The object to deallocate. */ void o_destroyJSONObject(JSONObject* json) { for(int i=0; inumberOfElements; i++) { libjson_destroyJSONPair(&json->elements[i]); } libjson_dealloc(json->elements); json->numberOfElements = 0; } // -- To String -- /** * Convert a json object to a string. * @warning Return value should be freed when no longer needed. * * @param json The object to convert to string. * @return A string representation of the object. */ char* o_JSONObjectToString(JSONObject json) { char* str = libjson_emptyString(1 + (json.numberOfElements == 0)); str[0] = '{'; if(json.numberOfElements == 0) { str[1] = '}'; } for(int i=0; inumberOfElements; i++) { JSONPair temp = json->elements[i]; if(libjson_strcmp(key, temp.key)) { index = i; exists = true; libjson_destroyJSONPair(&json->elements[i]); break; } } if(exists) { for(int i=index; inumberOfElements-1; i++) { json->elements[i] = json->elements[i+1]; } json->numberOfElements--; json->elements = realloc(json->elements, sizeof(JSONPair)*json->numberOfElements); } } /*============================================================================= JSONArray [] =============================================================================*/ // -- Constructor -- /** * Create an empty json array. * * @return An empty json array. */ JSONArray a_emptyJSONArray(void) { JSONArray json; json.numberOfElements = 0; json.elements = NULL; return json; } // -- Destructor -- /** * Free all heap memory used by a json array. * * @param json The array to deallocate. */ void a_destroyJSONArray(JSONArray* json) { for(int i=0; inumberOfElements; i++) { libjson_destroyJSONElement(&json->elements[i]); } libjson_dealloc(json->elements); json->numberOfElements = 0; } // -- To String -- /** * Convert a json array to a string. * @warning Return value should be freed when no longer needed. * * @param json The array to convert to string. * @return A string representation of the array. */ char* a_JSONArrayToString(JSONArray json) { char* str = libjson_emptyString(1 + (json.numberOfElements == 0)); str[0] = '['; if(json.numberOfElements == 0) { str[1] = ']'; } for(int i=0; inumberOfElements-1; i++) { json->elements[i] = json->elements[i+1]; } json->numberOfElements--; json->elements = realloc(json->elements, sizeof(JSONElement)*json->numberOfElements); } // -- Helper functions -- /** * Create an empty, or null, json value. * * @return An empty json value. */ static JSONElement libjson_emptyJSONElement(void) { JSONElement empty; empty.type = null; empty.object = o_emptyJSONObject(); empty.array = a_emptyJSONArray(); empty.boolean = false; empty.number = 0; empty.string = NULL; return empty; } /** * Free all heap memory used by a json value. * * @param element The value to deallocate. */ static void libjson_destroyJSONElement(JSONElement* element) { if(element->type == object) { o_destroyJSONObject(&element->object); } else if(element->type == array) { a_destroyJSONArray(&element->array); } else if(element->type == string) { libjson_dealloc(element->string); } element->type = null; } /** * Allocate more memory space at the end of a string. * @note Appended memory is initialized to null bytes. * @warning Return value should be freed when no longer needed. * * @param str The original string to append space after. * @param additionalSize The number of additioal bytes to allocate. * * @return The reallocated string, or NULL if it fails. */ static char* libjson_extendString(char* str, int additionalSize) { int len = libjson_strlen(str); int size = len + additionalSize + 1; char* extended = realloc(str, size); if(extended) { for(int i=len; i 0 && escapes % 2 == 0) { end = i; break; } if(c != '\\') { escapes = 0; } } char* s = libjson_emptyString(end-1); for(int i=1; ikey); libjson_destroyJSONElement(&pair->value); } /** * Get a value from a json object by it's key. * * @param json The object to get a value from. * @param key The key the value is paired with. * @return The value, or a json null if the key isn't present. */ static JSONElement o_getJSONElement(JSONObject json, char* key) { JSONElement element = libjson_emptyJSONElement(); for(int i=0; ielements, sizeof(JSONPair)*(json->numberOfElements + 1)); if(!tmp) { fprintf(stderr, "Ran out of memory in setJSONElement"); } else { json->elements = tmp; JSONPair pair; pair.value = set; pair.key = libjson_strcpy(key); json->elements[json->numberOfElements] = pair; json->numberOfElements++; } } /** * Convert a key/value pair to a string. * @warning Return value should be freed when no longer needed. * * @param pair The key/value pair to convert to string. * @return A string representation of the key/value pair. */ static char* libjson_JSONPairToString(JSONPair pair) { char* value = libjson_JSONElementToString(pair.value); char* str = libjson_emptyString(libjson_strlen(value)+3+libjson_strlen(pair.key)); sprintf(str, "\"%s\":%s", pair.key, value); libjson_dealloc(value); return str; } /** * Get a value from a json array at an index. * * @param json The array to get a value from. * @param index The index the value is located at. * @return The value, or a json null if the index is not in bounds. */ static JSONElement a_getJSONElement(JSONArray json, int index) { if(index < 0 || index >= json.numberOfElements) { return libjson_emptyJSONElement(); } return json.elements[index]; } /** * Add a value at an index to a json array. This will push other elements later into the array. * If the @p index is less than or equal to 0, the item will be put at the start of the array. * If the @p index is greater than or equal to the length of the array, the item will be put at * the end of the array. * * @param json The array to add the value to. * @param index The index to add the value at. * @param set The value to add. */ static void a_setJSONElement(JSONArray* json, int index, JSONElement set) { if(index < 0) { index = 0; } else if(index > json->numberOfElements) { index = json->numberOfElements; } JSONElement* tmp = realloc(json->elements, sizeof(JSONElement)*(json->numberOfElements + 1)); if(!tmp) { fprintf(stderr, "Ran out of memory in addJSONElement"); } else { for(int i=json->numberOfElements; i>index; i--) { json->elements[i] = json->elements[i-1]; } json->numberOfElements++; json->elements = tmp; json->elements[index] = set; } } /** * Free a pointer, and set it to NULL. * * @param p A pointer to the pointer to deallocate. */ static void libjson_deallocate(void** p) { if(p) { free(*p); *p = NULL; } } /** * Round a double down, truncating any decimal points off. * * @param d The double to floor. * @return The floored double. */ static long double libjson_floor(long double d) { return (long double)(long)d; } /** * Check if a character is a digit. (0-9) * * @param c The character to check. * @return If the character is a digit, true. Otherwise, false. */ static bool libjson_isdigit(char c) { return c >= '0' && c <= '9'; } /** * Check if a character is whitespace. * * @param c The character to check. * @return If the character is whitespace, true. Otherwise, false. */ static bool libjson_isspace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v'; } /** * Copy the contents of a string to another string. * * @param str The string to copy. * @return The copied string. */ static char* libjson_strcpy(char* str) { if(!str) { return NULL; } int len = libjson_strlen(str); char* cpy = libjson_emptyString(len); for(int i=0; i