From a2d5535324b615af8be45904a0cc3013f65e3cc5 Mon Sep 17 00:00:00 2001 From: Suhel Chakraborty Date: Sun, 9 Oct 2022 12:33:49 +0530 Subject: [PATCH 1/5] Fixed example (#14) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62e61ab..9097732 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ int main() { typed(json_element) element = result_unwrap(json_element)(&element_result); // Fetch the "hello" key value - result(json_element) hello_element_result = json_object_find(&element, "hello"); + result(json_element) hello_element_result = json_object_find(&element.value.as_object, "hello"); if(result_is_err(json_element)(&hello_element_result)) { typed(json_error) error = result_unwrap_err(json_element)(&hello_element_result); fprintf(stderr, "Error getting element \"hello\": %s\n", json_error_to_string(error)); From 75bab87465860dd3c9a1fec5db1bae7d1d5f9f2a Mon Sep 17 00:00:00 2001 From: Suhel Chakraborty Date: Fri, 22 Sep 2023 22:41:35 +0530 Subject: [PATCH 2/5] Cleanup --- .gitignore | 5 -- .vscode/launch.json | 27 -------- .vscode/settings.json | 4 +- .vscode/tasks.json | 25 ------- example.c | 1 - json.c | 150 ++++++++++++++++++++++++++---------------- json.h | 34 ---------- 7 files changed, 95 insertions(+), 151 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/tasks.json diff --git a/.gitignore b/.gitignore index d8d651a..ba077a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1 @@ -.vscode/**/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json bin diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 38c0473..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/bin/a.out", - "cwd": "${workspaceFolder}/bin", - "preLaunchTask": "Build", - "linux": { - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb" - }, - "osx": { - "MIMode": "lldb" - }, - "windows": { - "MIMode": "gdb", - "miDebuggerPath": "C:\\MinGw\\bin\\gdb.exe" - } - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json index 240a369..7bd09ff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,4 @@ { - "[json]": { - "editor.formatOnSave": false - }, + "editor.formatOnSave": true, "C_Cpp.clang_format_style": "LLVM" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index ae06ce6..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "tasks": [ - { - "type": "shell", - "label": "Build", - "command": "/usr/bin/clang", - "args": [ - "-fdiagnostics-color=always", - "-g", - "-O0", - "-DJSON_DEBUG", - "${workspaceFolder}/*.c", - "-o", - "${workspaceFolder}/bin/a.out" - ], - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": ["$gcc"], - "group": "build", - "detail": "C build task" - } - ], - "version": "2.0.0" -} diff --git a/example.c b/example.c index 42cb97c..776d6d4 100644 --- a/example.c +++ b/example.c @@ -49,7 +49,6 @@ int main() { return -1; } typed(json_element) element = result_unwrap(json_element)(&element_result); - typed(json_object) *obj = element.value.as_object; // json_print(&element, 2); json_free(&element); diff --git a/json.c b/json.c index d1d2178..64edfdb 100644 --- a/json.c +++ b/json.c @@ -1,15 +1,16 @@ #include "json.h" #include +#include +#include #include #include #include -#include -#ifdef JSON_SCRAPE_WHITESPACE -#define json_scrape_whitespace(arg) json_skip_whitespace(arg) +#ifdef JSON_SKIP_WHITESPACE +#define json_skip_whitespace(arg) json_skip_whitespace_actual(arg) #else -#define json_scrape_whitespace(arg) +#define json_skip_whitespace(arg) #endif #ifdef JSON_DEBUG @@ -18,6 +19,40 @@ #define log(str, ...) #endif +#define define_result_type(name) \ + result(name) result_ok(name)(typed(name) value) { \ + result(name) retval = { \ + .is_ok = true, \ + .inner = \ + { \ + .value = value, \ + }, \ + }; \ + return retval; \ + } \ + result(name) result_err(name)(typed(json_error) err) { \ + result(name) retval = { \ + .is_ok = false, \ + .inner = \ + { \ + .err = err, \ + }, \ + }; \ + return retval; \ + } \ + typed(json_boolean) result_is_ok(name)(result(name) * result) { \ + return result->is_ok; \ + } \ + typed(json_boolean) result_is_err(name)(result(name) * result) { \ + return !result->is_ok; \ + } \ + typed(name) result_unwrap(name)(result(name) * result) { \ + return result->inner.value; \ + } \ + typed(json_error) result_unwrap_err(name)(result(name) * result) { \ + return result->inner.err; \ + } + /** * @brief Allocate `count` number of items of `type` in memory * and return the pointer to the newly allocated memory @@ -182,7 +217,7 @@ static bool json_skip_boolean(typed(json_string) *); /** * @brief Moves a JSON string pointer beyond any whitespace */ -static void json_skip_whitespace(typed(json_string) *); +static void json_skip_whitespace_actual(typed(json_string) *); /** * @brief Moves a JSON string pointer beyond `null` literal @@ -276,12 +311,12 @@ result(json_element) json_parse(typed(json_string) json_str) { result(json_entry) json_parse_entry(typed(json_string) * str_ptr) { result_try(json_entry, json_element_value, key, json_parse_string(str_ptr)); - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Skip the ':' delimiter (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); result(json_element_type) type_result = json_guess_element_type(*str_ptr); if (result_is_err(json_element_type)(&type_result)) { @@ -366,6 +401,8 @@ result(json_element_value) case JSON_ELEMENT_TYPE_NULL: json_skip_null(str_ptr); return result_err(json_element_value)(JSON_ERROR_EMPTY); + default: + return result_err(json_element_value)(JSON_ERROR_INVALID_TYPE); } } @@ -393,8 +430,8 @@ result(json_element_value) json_parse_number(typed(json_string) * str_ptr) { typed(json_string) temp_str = *str_ptr; bool has_decimal = false; - while(json_is_number(*temp_str)) { - if(*temp_str == '.') { + while (json_is_number(*temp_str)) { + if (*temp_str == '.') { has_decimal = true; } @@ -403,26 +440,26 @@ result(json_element_value) json_parse_number(typed(json_string) * str_ptr) { typed(json_number) number = {}; -if(has_decimal) { - errno = 0; - - number.type = JSON_NUMBER_TYPE_DOUBLE; - number.value = (typed(json_number_value)) strtod(*str_ptr, (char **)str_ptr); + if (has_decimal) { + errno = 0; - if (errno == EINVAL || errno == ERANGE) - return result_err(json_element_value)(JSON_ERROR_INVALID_VALUE); -} else { - errno = 0; + number.type = JSON_NUMBER_TYPE_DOUBLE; + number.value = (typed(json_number_value))strtod(*str_ptr, (char **)str_ptr); - number.type = JSON_NUMBER_TYPE_LONG; - number.value = (typed(json_number_value)) strtol(*str_ptr, (char **) str_ptr, 10); + if (errno == EINVAL || errno == ERANGE) + return result_err(json_element_value)(JSON_ERROR_INVALID_VALUE); + } else { + errno = 0; - if(errno == EINVAL || errno == ERANGE) - return result_err(json_element_value)(JSON_ERROR_INVALID_VALUE); + number.type = JSON_NUMBER_TYPE_LONG; + number.value = + (typed(json_number_value))strtol(*str_ptr, (char **)str_ptr, 10); -} + if (errno == EINVAL || errno == ERANGE) + return result_err(json_element_value)(JSON_ERROR_INVALID_VALUE); + } -return result_ok(json_element_value)((typed(json_element_value))number); + return result_ok(json_element_value)((typed(json_element_value))number); } result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { @@ -432,7 +469,7 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { // Skip the first '{' character temp_str++; - json_scrape_whitespace(&temp_str); + json_skip_whitespace(&temp_str); if (*temp_str == '}') { // Skip the end '}' in the actual pointer @@ -444,15 +481,15 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { while (*temp_str != '\0') { // Skip any accidental whitespace - json_scrape_whitespace(&temp_str); + json_skip_whitespace(&temp_str); - char prev = *temp_str; + // If the entry could be skipped if (json_skip_entry(&temp_str)) { count++; } // Skip any accidental whitespace - json_scrape_whitespace(&temp_str); + json_skip_whitespace(&temp_str); if (*temp_str == '}') break; @@ -467,17 +504,17 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { // ******* Initialize the hash map ******* // Now we have a perfectly sized hash map typed(json_entry) **entries = allocN(typed(json_entry) *, count); - for (int i = 0; i < count; i++) + for (size_t i = 0; i < count; i++) entries[i] = NULL; // Skip the first '{' character (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); while (**str_ptr != '\0') { // Skip any accidental whitespace - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); result(json_entry) entry_result = json_parse_entry(str_ptr); if (result_is_ok(json_entry)(&entry_result)) { @@ -486,7 +523,7 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { // Bucket size is exactly count. So there will be at max // count misses in the worst case - for (int i = 0; i < count; i++) { + for (size_t i = 0; i < count; i++) { if (entries[bucket] == NULL) { typed(json_entry) *temp_entry = alloc(typed(json_entry)); memcpy(temp_entry, &entry, sizeof(typed(json_entry))); @@ -499,7 +536,7 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { } // Skip any accidental whitespace - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); if (**str_ptr == '}') break; @@ -531,7 +568,7 @@ result(json_element_value) json_parse_array(typed(json_string) * str_ptr) { // Skip the starting '[' character (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Unfortunately the array is empty if (**str_ptr == ']') { @@ -544,7 +581,7 @@ result(json_element_value) json_parse_array(typed(json_string) * str_ptr) { typed(json_element) *elements = NULL; while (**str_ptr != '\0') { - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Guess the type result(json_element_type) type_result = json_guess_element_type(*str_ptr); @@ -565,7 +602,7 @@ result(json_element_value) json_parse_array(typed(json_string) * str_ptr) { elements[count - 1].value = value; } - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); } // Reached the end @@ -616,7 +653,7 @@ result(json_element) // Bucket size is exactly obj->count. So there will be at max // obj->count misses in the worst case - for (int i = 0; i < obj->count; i++) { + for (size_t i = 0; i < obj->count; i++) { typed(json_entry) *entry = obj->entries[bucket]; if (strcmp(key, entry->key) == 0) return result_ok(json_element)(entry->element); @@ -630,12 +667,12 @@ result(json_element) bool json_skip_entry(typed(json_string) * str_ptr) { json_skip_string(str_ptr); - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Skip the ':' delimiter (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); result(json_element_type) type_result = json_guess_element_type(*str_ptr); if (result_is_err(json_element_type)(&type_result)) @@ -694,7 +731,7 @@ bool json_skip_object(typed(json_string) * str_ptr) { // Skip the first '{' character (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); if (**str_ptr == '}') { // Skip the end '}' @@ -704,12 +741,12 @@ bool json_skip_object(typed(json_string) * str_ptr) { while (**str_ptr != '\0') { // Skip any accidental whitespace - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); json_skip_entry(str_ptr); // Skip any accidental whitespace - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); if (**str_ptr == '}') break; @@ -728,7 +765,7 @@ bool json_skip_array(typed(json_string) * str_ptr) { // Skip the starting '[' character (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Unfortunately the array is empty if (**str_ptr == ']') { @@ -738,7 +775,7 @@ bool json_skip_array(typed(json_string) * str_ptr) { } while (**str_ptr != '\0') { - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Guess the type result(json_element_type) type_result = json_guess_element_type(*str_ptr); @@ -749,7 +786,7 @@ bool json_skip_array(typed(json_string) * str_ptr) { // Parse the value based on guessed type json_skip_element_value(str_ptr, type); - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); } // Reached the end @@ -780,7 +817,7 @@ bool json_skip_boolean(typed(json_string) * str_ptr) { return false; } -void json_skip_whitespace(typed(json_string) * str_ptr) { +void json_skip_whitespace_actual(typed(json_string) * str_ptr) { while (is_whitespace(**str_ptr)) (*str_ptr)++; } @@ -819,12 +856,12 @@ void json_print_element(typed(json_element) * element, int indent, void json_print_string(typed(json_string) string) { printf("\"%s\"", string); } void json_print_number(typed(json_number) number) { - switch(number.type) { - case JSON_NUMBER_TYPE_DOUBLE: + switch (number.type) { + case JSON_NUMBER_TYPE_DOUBLE: printf("%f", number.value.as_double); break; - case JSON_NUMBER_TYPE_LONG: + case JSON_NUMBER_TYPE_LONG: printf("%ld", number.value.as_long); break; } @@ -834,7 +871,7 @@ void json_print_object(typed(json_object) * object, int indent, int indent_level) { printf("{\n"); - for (int i = 0; i < object->count; i++) { + for (size_t i = 0; i < object->count; i++) { for (int j = 0; j < indent * (indent_level + 1); j++) printf(" "); @@ -857,7 +894,7 @@ void json_print_object(typed(json_object) * object, int indent, void json_print_array(typed(json_array) * array, int indent, int indent_level) { printf("[\n"); - for (int i = 0; i < array->count; i++) { + for (size_t i = 0; i < array->count; i++) { typed(json_element) element = array->elements[i]; for (int j = 0; j < indent * (indent_level + 1); j++) printf(" "); @@ -910,12 +947,13 @@ void json_free_object(typed(json_object) * object) { return; } - for (int i = 0; i < object->count; i++) { + for (size_t i = 0; i < object->count; i++) { typed(json_entry) *entry = object->entries[i]; if (entry != NULL) { free((void *)entry->key); json_free(&entry->element); + free(entry); } } @@ -933,7 +971,7 @@ void json_free_array(typed(json_array) * array) { } // Recursively free each element in the array - for (int i = 0; i < array->count; i++) { + for (size_t i = 0; i < array->count; i++) { typed(json_element) element = array->elements[i]; json_free(&element); } @@ -983,7 +1021,7 @@ result(json_string) typed(size) count = 0; typed(json_string) iter = str; - while (iter - str < len) { + while ((size_t)(iter - str) < len) { if (*iter == '\\') iter++; @@ -995,7 +1033,7 @@ result(json_string) typed(size) offset = 0; iter = str; - while (iter - str < len) { + while ((size_t)(iter - str) < len) { if (*iter == '\\') { iter++; @@ -1037,7 +1075,7 @@ result(json_string) } void json_debug_print(typed(json_string) str, typed(size) len) { - for (int i = 0; i < len; i++) { + for (size_t i = 0; i < len; i++) { if (str[i] == '\0') break; diff --git a/json.h b/json.h index a7e2b3c..3e5d4d9 100644 --- a/json.h +++ b/json.h @@ -55,40 +55,6 @@ typedef struct json_array_s typed(json_array); typed(name) result_unwrap(name)(result(name) *); \ typed(json_error) result_unwrap_err(name)(result(name) *); -#define define_result_type(name) \ - result(name) result_ok(name)(typed(name) value) { \ - result(name) retval = { \ - .is_ok = true, \ - .inner = \ - { \ - .value = value, \ - }, \ - }; \ - return retval; \ - } \ - result(name) result_err(name)(typed(json_error) err) { \ - result(name) retval = { \ - .is_ok = false, \ - .inner = \ - { \ - .err = err, \ - }, \ - }; \ - return retval; \ - } \ - typed(json_boolean) result_is_ok(name)(result(name) * result) { \ - return result->is_ok; \ - } \ - typed(json_boolean) result_is_err(name)(result(name) * result) { \ - return !result->is_ok; \ - } \ - typed(name) result_unwrap(name)(result(name) * result) { \ - return result->inner.value; \ - } \ - typed(json_error) result_unwrap_err(name)(result(name) * result) { \ - return result->inner.err; \ - } - enum json_element_type_e { JSON_ELEMENT_TYPE_STRING = 0, JSON_ELEMENT_TYPE_NUMBER, From 738652586269ef10c024b39a1a2ee89667cd2905 Mon Sep 17 00:00:00 2001 From: Suhel Chakraborty Date: Fri, 22 Sep 2023 22:42:33 +0530 Subject: [PATCH 3/5] Updated README --- README.md | 80 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9097732..f8ed5e1 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,19 @@ An easy to use, very fast JSON parsing implementation written in pure C ## Features - * Fully [RFC-8259](https://datatracker.ietf.org/doc/html/rfc8259) compliant - * Small 2 file library - * Support for all data types - * Simple and efficient hash table implementation to search element by key - * Rust like `result` type used throughout fallible calls - * Compile with `-DJSON_SCRAPE_WHITESPACE` to parse non-minified JSON with whitespace in between +- Fully [RFC-8259](https://datatracker.ietf.org/doc/html/rfc8259) compliant +- Small 2 file library +- Support for all data types +- Simple and efficient hash table implementation to search element by key +- Rust like `result` type used throughout fallible calls +- Compile with `-DJSON_SKIP_WHITESPACE` to parse non-minified JSON with whitespace in between ## Setup Copy the following from this repository in your source - * `json.h` - * `json.c` +- `json.h` +- `json.c` And in your code @@ -25,7 +25,9 @@ And in your code ``` ## MACROs + ### The `typed` helper + A uniform type system used throughout the API `typed(x)` is alias for `x_t` @@ -34,6 +36,7 @@ typed(json_element) // json_element_t ``` ### Rust like `result` + A tagged union comprising two variants, either `ok` with the data or `err` with `typed(json_error)` with simplified API to manage variants ```C @@ -43,90 +46,118 @@ result(json_element) // json_element_result_t ## API ### Parse JSON: + ```C result(json_element) json_parse(typed(json_string) json_str); ``` ### Find an element by key + ```C result(json_element) json_object_find(typed(json_object) * object, typed(json_string) key); ``` ### Print JSON with specified indentation + ```C void json_print(typed(json_element) *element, int indent); ``` ### Free JSON from memory + ```C void json_free(typed(json_element) *element); ``` ### Convert error into user friendly error String + ```C typed(json_string) json_error_to_string(typed(json_error) error); ``` ## Types + ### JSON String + A null-terminated char-sequence + ```C typed(json_string) // alias for const char * ``` ### JSON Number + A 64-bit floating point number + ```C typed(json_number) // alias for double ``` ### JSON Object + An array of key-value entries + ```C typed(json_object) ``` + #### Fields + | **Name** | **Type** | **Description** | -|-----------|-----------------------|-----------------------| +| --------- | --------------------- | --------------------- | | `count` | `typed(size)` | The number of entries | | `entries` | `typed(json_entry) *` | The array of entries | ### JSON Array + A hetergeneous array of elements + ```C typed(json_array) ``` + #### Fields + | **Name** | **Type** | **Description** | -|------------|-------------------------|------------------------| +| ---------- | ----------------------- | ---------------------- | | `count` | `typed(size)` | The number of elements | | `elements` | `typed(json_element) *` | The array of elements | ### JSON Boolean + A boolean value + ```C typed(json_boolean) ``` ### Element + A tagged union representing a JSON value with its type + ```C typed(json_element) ``` + #### Fields + | **Name** | **Type** | **Description** | -|----------|-----------------------------|-----------------------| +| -------- | --------------------------- | --------------------- | | `type` | `typed(json_element_type)` | The type of the value | | `value` | `typed(json_element_value)` | The actual value | ### Element Type + An enum which represents a JSON type + ```C typed(json_element_type) ``` + #### Variants + | **Variant** | **Description** | -|-----------------------------|-----------------| +| --------------------------- | --------------- | | `JSON_ELEMENT_TYPE_STRING` | JSON String | | `JSON_ELEMENT_TYPE_NUMBER` | JSON Number | | `JSON_ELEMENT_TYPE_OBJECT` | JSON Object | @@ -135,13 +166,17 @@ typed(json_element_type) | `JSON_ELEMENT_TYPE_NULL` | JSON Null | ### Element Value + A union for interpreting JSON data + ```C typed(json_element_value) ``` + #### Fields + | **Name** | **Type** | **Interpret data as** | -|--------------|------------------------|-----------------------| +| ------------ | ---------------------- | --------------------- | | `as_string` | `typed(json_string)` | JSON String | | `as_number` | `typed(json_number)` | JSON Number | | `as_object` | `typed(json_object) *` | JSON Object | @@ -149,13 +184,17 @@ typed(json_element_value) | `as_boolean` | `typed(json_boolean)` | JSON Boolean | ### Error + An enum which represents an error + ```C typed(json_error) ``` + #### Variants + | **Variant** | **Description** | -|----------------------------|--------------------------------| +| -------------------------- | ------------------------------ | | `JSON_ERROR_EMPTY` | Null or empty value | | `JSON_ERROR_INVALID_TYPE` | Type inference failed | | `JSON_ERROR_INVALID_KEY` | Key is not a valid string | @@ -173,7 +212,7 @@ const char * some_json_str = "{\"hello\":\"world\",\"key\":\"value\"}"; int main() { result(json_element) element_result = json_parse(some_json_str); - // Guard if + // Guard if if(result_is_err(json_element)(&element_result)) { typed(json_error) error = result_unwrap_err(json_element)(&element_result); fprintf(stderr, "Error parsing JSON: %s\n", json_error_to_string(error)); @@ -184,7 +223,7 @@ int main() { typed(json_element) element = result_unwrap(json_element)(&element_result); // Fetch the "hello" key value - result(json_element) hello_element_result = json_object_find(&element.value.as_object, "hello"); + result(json_element) hello_element_result = json_object_find(element.value.as_object, "hello"); if(result_is_err(json_element)(&hello_element_result)) { typed(json_error) error = result_unwrap_err(json_element)(&hello_element_result); fprintf(stderr, "Error getting element \"hello\": %s\n", json_error_to_string(error)); @@ -200,6 +239,7 @@ int main() { return 0; } ``` + Outputs ``` @@ -212,9 +252,9 @@ Outputs ### Example in repository - 1. Clone this repository `git clone https://github.com/forkachild/C-Simple-JSON-Parser` - 2. Compile the example `clang example.c json.c -o example.out` - 3. Run the binary `./example.out` +1. Clone this repository `git clone https://github.com/forkachild/C-Simple-JSON-Parser` +2. Compile the example `clang example.c json.c -o example.out` +3. Run the binary `./example.out` ## FAQs @@ -298,6 +338,6 @@ for(i = 0; i < arr->count; i++) { ### What if the JSON is poorly formatted with uneven whitespace -Compile using `-DJSON_SCRAPE_WHITESPACE` +Compile using `-DJSON_SKIP_WHITESPACE` ## If this helped you in any way you can [buy me a beer](https://www.paypal.me/suhelchakraborty) From 074c2273af84c1ff5751034b9d2f4deeb2b5ce24 Mon Sep 17 00:00:00 2001 From: Suhel Chakraborty Date: Fri, 22 Sep 2023 22:41:35 +0530 Subject: [PATCH 4/5] Cleanup --- .gitignore | 5 -- .vscode/launch.json | 27 -------- .vscode/settings.json | 4 +- .vscode/tasks.json | 25 ------- example.c | 1 - json.c | 149 ++++++++++++++++++++++++++---------------- json.h | 34 ---------- 7 files changed, 94 insertions(+), 151 deletions(-) delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/tasks.json diff --git a/.gitignore b/.gitignore index d8d651a..ba077a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1 @@ -.vscode/**/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json bin diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 38c0473..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/bin/a.out", - "cwd": "${workspaceFolder}/bin", - "preLaunchTask": "Build", - "linux": { - "MIMode": "gdb", - "miDebuggerPath": "/usr/bin/gdb" - }, - "osx": { - "MIMode": "lldb" - }, - "windows": { - "MIMode": "gdb", - "miDebuggerPath": "C:\\MinGw\\bin\\gdb.exe" - } - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json index 240a369..7bd09ff 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,4 @@ { - "[json]": { - "editor.formatOnSave": false - }, + "editor.formatOnSave": true, "C_Cpp.clang_format_style": "LLVM" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index ae06ce6..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "tasks": [ - { - "type": "shell", - "label": "Build", - "command": "/usr/bin/clang", - "args": [ - "-fdiagnostics-color=always", - "-g", - "-O0", - "-DJSON_DEBUG", - "${workspaceFolder}/*.c", - "-o", - "${workspaceFolder}/bin/a.out" - ], - "options": { - "cwd": "${workspaceFolder}" - }, - "problemMatcher": ["$gcc"], - "group": "build", - "detail": "C build task" - } - ], - "version": "2.0.0" -} diff --git a/example.c b/example.c index 42cb97c..776d6d4 100644 --- a/example.c +++ b/example.c @@ -49,7 +49,6 @@ int main() { return -1; } typed(json_element) element = result_unwrap(json_element)(&element_result); - typed(json_object) *obj = element.value.as_object; // json_print(&element, 2); json_free(&element); diff --git a/json.c b/json.c index 8905fd2..64edfdb 100644 --- a/json.c +++ b/json.c @@ -1,15 +1,16 @@ #include "json.h" #include +#include +#include #include #include #include -#include -#ifdef JSON_SCRAPE_WHITESPACE -#define json_scrape_whitespace(arg) json_skip_whitespace(arg) +#ifdef JSON_SKIP_WHITESPACE +#define json_skip_whitespace(arg) json_skip_whitespace_actual(arg) #else -#define json_scrape_whitespace(arg) +#define json_skip_whitespace(arg) #endif #ifdef JSON_DEBUG @@ -18,6 +19,40 @@ #define log(str, ...) #endif +#define define_result_type(name) \ + result(name) result_ok(name)(typed(name) value) { \ + result(name) retval = { \ + .is_ok = true, \ + .inner = \ + { \ + .value = value, \ + }, \ + }; \ + return retval; \ + } \ + result(name) result_err(name)(typed(json_error) err) { \ + result(name) retval = { \ + .is_ok = false, \ + .inner = \ + { \ + .err = err, \ + }, \ + }; \ + return retval; \ + } \ + typed(json_boolean) result_is_ok(name)(result(name) * result) { \ + return result->is_ok; \ + } \ + typed(json_boolean) result_is_err(name)(result(name) * result) { \ + return !result->is_ok; \ + } \ + typed(name) result_unwrap(name)(result(name) * result) { \ + return result->inner.value; \ + } \ + typed(json_error) result_unwrap_err(name)(result(name) * result) { \ + return result->inner.err; \ + } + /** * @brief Allocate `count` number of items of `type` in memory * and return the pointer to the newly allocated memory @@ -182,7 +217,7 @@ static bool json_skip_boolean(typed(json_string) *); /** * @brief Moves a JSON string pointer beyond any whitespace */ -static void json_skip_whitespace(typed(json_string) *); +static void json_skip_whitespace_actual(typed(json_string) *); /** * @brief Moves a JSON string pointer beyond `null` literal @@ -276,12 +311,12 @@ result(json_element) json_parse(typed(json_string) json_str) { result(json_entry) json_parse_entry(typed(json_string) * str_ptr) { result_try(json_entry, json_element_value, key, json_parse_string(str_ptr)); - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Skip the ':' delimiter (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); result(json_element_type) type_result = json_guess_element_type(*str_ptr); if (result_is_err(json_element_type)(&type_result)) { @@ -366,6 +401,8 @@ result(json_element_value) case JSON_ELEMENT_TYPE_NULL: json_skip_null(str_ptr); return result_err(json_element_value)(JSON_ERROR_EMPTY); + default: + return result_err(json_element_value)(JSON_ERROR_INVALID_TYPE); } } @@ -393,8 +430,8 @@ result(json_element_value) json_parse_number(typed(json_string) * str_ptr) { typed(json_string) temp_str = *str_ptr; bool has_decimal = false; - while(json_is_number(*temp_str)) { - if(*temp_str == '.') { + while (json_is_number(*temp_str)) { + if (*temp_str == '.') { has_decimal = true; } @@ -403,26 +440,26 @@ result(json_element_value) json_parse_number(typed(json_string) * str_ptr) { typed(json_number) number = {}; -if(has_decimal) { - errno = 0; - - number.type = JSON_NUMBER_TYPE_DOUBLE; - number.value = (typed(json_number_value)) strtod(*str_ptr, (char **)str_ptr); + if (has_decimal) { + errno = 0; - if (errno == EINVAL || errno == ERANGE) - return result_err(json_element_value)(JSON_ERROR_INVALID_VALUE); -} else { - errno = 0; + number.type = JSON_NUMBER_TYPE_DOUBLE; + number.value = (typed(json_number_value))strtod(*str_ptr, (char **)str_ptr); - number.type = JSON_NUMBER_TYPE_LONG; - number.value = (typed(json_number_value)) strtol(*str_ptr, (char **) str_ptr, 10); + if (errno == EINVAL || errno == ERANGE) + return result_err(json_element_value)(JSON_ERROR_INVALID_VALUE); + } else { + errno = 0; - if(errno == EINVAL || errno == ERANGE) - return result_err(json_element_value)(JSON_ERROR_INVALID_VALUE); + number.type = JSON_NUMBER_TYPE_LONG; + number.value = + (typed(json_number_value))strtol(*str_ptr, (char **)str_ptr, 10); -} + if (errno == EINVAL || errno == ERANGE) + return result_err(json_element_value)(JSON_ERROR_INVALID_VALUE); + } -return result_ok(json_element_value)((typed(json_element_value))number); + return result_ok(json_element_value)((typed(json_element_value))number); } result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { @@ -432,7 +469,7 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { // Skip the first '{' character temp_str++; - json_scrape_whitespace(&temp_str); + json_skip_whitespace(&temp_str); if (*temp_str == '}') { // Skip the end '}' in the actual pointer @@ -444,15 +481,15 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { while (*temp_str != '\0') { // Skip any accidental whitespace - json_scrape_whitespace(&temp_str); + json_skip_whitespace(&temp_str); - char prev = *temp_str; + // If the entry could be skipped if (json_skip_entry(&temp_str)) { count++; } // Skip any accidental whitespace - json_scrape_whitespace(&temp_str); + json_skip_whitespace(&temp_str); if (*temp_str == '}') break; @@ -467,17 +504,17 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { // ******* Initialize the hash map ******* // Now we have a perfectly sized hash map typed(json_entry) **entries = allocN(typed(json_entry) *, count); - for (int i = 0; i < count; i++) + for (size_t i = 0; i < count; i++) entries[i] = NULL; // Skip the first '{' character (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); while (**str_ptr != '\0') { // Skip any accidental whitespace - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); result(json_entry) entry_result = json_parse_entry(str_ptr); if (result_is_ok(json_entry)(&entry_result)) { @@ -486,7 +523,7 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { // Bucket size is exactly count. So there will be at max // count misses in the worst case - for (int i = 0; i < count; i++) { + for (size_t i = 0; i < count; i++) { if (entries[bucket] == NULL) { typed(json_entry) *temp_entry = alloc(typed(json_entry)); memcpy(temp_entry, &entry, sizeof(typed(json_entry))); @@ -499,7 +536,7 @@ result(json_element_value) json_parse_object(typed(json_string) * str_ptr) { } // Skip any accidental whitespace - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); if (**str_ptr == '}') break; @@ -531,7 +568,7 @@ result(json_element_value) json_parse_array(typed(json_string) * str_ptr) { // Skip the starting '[' character (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Unfortunately the array is empty if (**str_ptr == ']') { @@ -544,7 +581,7 @@ result(json_element_value) json_parse_array(typed(json_string) * str_ptr) { typed(json_element) *elements = NULL; while (**str_ptr != '\0') { - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Guess the type result(json_element_type) type_result = json_guess_element_type(*str_ptr); @@ -565,7 +602,7 @@ result(json_element_value) json_parse_array(typed(json_string) * str_ptr) { elements[count - 1].value = value; } - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); } // Reached the end @@ -616,7 +653,7 @@ result(json_element) // Bucket size is exactly obj->count. So there will be at max // obj->count misses in the worst case - for (int i = 0; i < obj->count; i++) { + for (size_t i = 0; i < obj->count; i++) { typed(json_entry) *entry = obj->entries[bucket]; if (strcmp(key, entry->key) == 0) return result_ok(json_element)(entry->element); @@ -630,12 +667,12 @@ result(json_element) bool json_skip_entry(typed(json_string) * str_ptr) { json_skip_string(str_ptr); - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Skip the ':' delimiter (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); result(json_element_type) type_result = json_guess_element_type(*str_ptr); if (result_is_err(json_element_type)(&type_result)) @@ -694,7 +731,7 @@ bool json_skip_object(typed(json_string) * str_ptr) { // Skip the first '{' character (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); if (**str_ptr == '}') { // Skip the end '}' @@ -704,12 +741,12 @@ bool json_skip_object(typed(json_string) * str_ptr) { while (**str_ptr != '\0') { // Skip any accidental whitespace - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); json_skip_entry(str_ptr); // Skip any accidental whitespace - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); if (**str_ptr == '}') break; @@ -728,7 +765,7 @@ bool json_skip_array(typed(json_string) * str_ptr) { // Skip the starting '[' character (*str_ptr)++; - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Unfortunately the array is empty if (**str_ptr == ']') { @@ -738,7 +775,7 @@ bool json_skip_array(typed(json_string) * str_ptr) { } while (**str_ptr != '\0') { - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); // Guess the type result(json_element_type) type_result = json_guess_element_type(*str_ptr); @@ -749,7 +786,7 @@ bool json_skip_array(typed(json_string) * str_ptr) { // Parse the value based on guessed type json_skip_element_value(str_ptr, type); - json_scrape_whitespace(str_ptr); + json_skip_whitespace(str_ptr); } // Reached the end @@ -780,7 +817,7 @@ bool json_skip_boolean(typed(json_string) * str_ptr) { return false; } -void json_skip_whitespace(typed(json_string) * str_ptr) { +void json_skip_whitespace_actual(typed(json_string) * str_ptr) { while (is_whitespace(**str_ptr)) (*str_ptr)++; } @@ -819,12 +856,12 @@ void json_print_element(typed(json_element) * element, int indent, void json_print_string(typed(json_string) string) { printf("\"%s\"", string); } void json_print_number(typed(json_number) number) { - switch(number.type) { - case JSON_NUMBER_TYPE_DOUBLE: + switch (number.type) { + case JSON_NUMBER_TYPE_DOUBLE: printf("%f", number.value.as_double); break; - case JSON_NUMBER_TYPE_LONG: + case JSON_NUMBER_TYPE_LONG: printf("%ld", number.value.as_long); break; } @@ -834,7 +871,7 @@ void json_print_object(typed(json_object) * object, int indent, int indent_level) { printf("{\n"); - for (int i = 0; i < object->count; i++) { + for (size_t i = 0; i < object->count; i++) { for (int j = 0; j < indent * (indent_level + 1); j++) printf(" "); @@ -857,7 +894,7 @@ void json_print_object(typed(json_object) * object, int indent, void json_print_array(typed(json_array) * array, int indent, int indent_level) { printf("[\n"); - for (int i = 0; i < array->count; i++) { + for (size_t i = 0; i < array->count; i++) { typed(json_element) element = array->elements[i]; for (int j = 0; j < indent * (indent_level + 1); j++) printf(" "); @@ -910,7 +947,7 @@ void json_free_object(typed(json_object) * object) { return; } - for (int i = 0; i < object->count; i++) { + for (size_t i = 0; i < object->count; i++) { typed(json_entry) *entry = object->entries[i]; if (entry != NULL) { @@ -934,7 +971,7 @@ void json_free_array(typed(json_array) * array) { } // Recursively free each element in the array - for (int i = 0; i < array->count; i++) { + for (size_t i = 0; i < array->count; i++) { typed(json_element) element = array->elements[i]; json_free(&element); } @@ -984,7 +1021,7 @@ result(json_string) typed(size) count = 0; typed(json_string) iter = str; - while (iter - str < len) { + while ((size_t)(iter - str) < len) { if (*iter == '\\') iter++; @@ -996,7 +1033,7 @@ result(json_string) typed(size) offset = 0; iter = str; - while (iter - str < len) { + while ((size_t)(iter - str) < len) { if (*iter == '\\') { iter++; @@ -1038,7 +1075,7 @@ result(json_string) } void json_debug_print(typed(json_string) str, typed(size) len) { - for (int i = 0; i < len; i++) { + for (size_t i = 0; i < len; i++) { if (str[i] == '\0') break; diff --git a/json.h b/json.h index a7e2b3c..3e5d4d9 100644 --- a/json.h +++ b/json.h @@ -55,40 +55,6 @@ typedef struct json_array_s typed(json_array); typed(name) result_unwrap(name)(result(name) *); \ typed(json_error) result_unwrap_err(name)(result(name) *); -#define define_result_type(name) \ - result(name) result_ok(name)(typed(name) value) { \ - result(name) retval = { \ - .is_ok = true, \ - .inner = \ - { \ - .value = value, \ - }, \ - }; \ - return retval; \ - } \ - result(name) result_err(name)(typed(json_error) err) { \ - result(name) retval = { \ - .is_ok = false, \ - .inner = \ - { \ - .err = err, \ - }, \ - }; \ - return retval; \ - } \ - typed(json_boolean) result_is_ok(name)(result(name) * result) { \ - return result->is_ok; \ - } \ - typed(json_boolean) result_is_err(name)(result(name) * result) { \ - return !result->is_ok; \ - } \ - typed(name) result_unwrap(name)(result(name) * result) { \ - return result->inner.value; \ - } \ - typed(json_error) result_unwrap_err(name)(result(name) * result) { \ - return result->inner.err; \ - } - enum json_element_type_e { JSON_ELEMENT_TYPE_STRING = 0, JSON_ELEMENT_TYPE_NUMBER, From 79b27bed9770645fba050aacfb72332546319ed9 Mon Sep 17 00:00:00 2001 From: Suhel Chakraborty Date: Fri, 22 Sep 2023 22:42:33 +0530 Subject: [PATCH 5/5] Updated README --- README.md | 80 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 9097732..f8ed5e1 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,19 @@ An easy to use, very fast JSON parsing implementation written in pure C ## Features - * Fully [RFC-8259](https://datatracker.ietf.org/doc/html/rfc8259) compliant - * Small 2 file library - * Support for all data types - * Simple and efficient hash table implementation to search element by key - * Rust like `result` type used throughout fallible calls - * Compile with `-DJSON_SCRAPE_WHITESPACE` to parse non-minified JSON with whitespace in between +- Fully [RFC-8259](https://datatracker.ietf.org/doc/html/rfc8259) compliant +- Small 2 file library +- Support for all data types +- Simple and efficient hash table implementation to search element by key +- Rust like `result` type used throughout fallible calls +- Compile with `-DJSON_SKIP_WHITESPACE` to parse non-minified JSON with whitespace in between ## Setup Copy the following from this repository in your source - * `json.h` - * `json.c` +- `json.h` +- `json.c` And in your code @@ -25,7 +25,9 @@ And in your code ``` ## MACROs + ### The `typed` helper + A uniform type system used throughout the API `typed(x)` is alias for `x_t` @@ -34,6 +36,7 @@ typed(json_element) // json_element_t ``` ### Rust like `result` + A tagged union comprising two variants, either `ok` with the data or `err` with `typed(json_error)` with simplified API to manage variants ```C @@ -43,90 +46,118 @@ result(json_element) // json_element_result_t ## API ### Parse JSON: + ```C result(json_element) json_parse(typed(json_string) json_str); ``` ### Find an element by key + ```C result(json_element) json_object_find(typed(json_object) * object, typed(json_string) key); ``` ### Print JSON with specified indentation + ```C void json_print(typed(json_element) *element, int indent); ``` ### Free JSON from memory + ```C void json_free(typed(json_element) *element); ``` ### Convert error into user friendly error String + ```C typed(json_string) json_error_to_string(typed(json_error) error); ``` ## Types + ### JSON String + A null-terminated char-sequence + ```C typed(json_string) // alias for const char * ``` ### JSON Number + A 64-bit floating point number + ```C typed(json_number) // alias for double ``` ### JSON Object + An array of key-value entries + ```C typed(json_object) ``` + #### Fields + | **Name** | **Type** | **Description** | -|-----------|-----------------------|-----------------------| +| --------- | --------------------- | --------------------- | | `count` | `typed(size)` | The number of entries | | `entries` | `typed(json_entry) *` | The array of entries | ### JSON Array + A hetergeneous array of elements + ```C typed(json_array) ``` + #### Fields + | **Name** | **Type** | **Description** | -|------------|-------------------------|------------------------| +| ---------- | ----------------------- | ---------------------- | | `count` | `typed(size)` | The number of elements | | `elements` | `typed(json_element) *` | The array of elements | ### JSON Boolean + A boolean value + ```C typed(json_boolean) ``` ### Element + A tagged union representing a JSON value with its type + ```C typed(json_element) ``` + #### Fields + | **Name** | **Type** | **Description** | -|----------|-----------------------------|-----------------------| +| -------- | --------------------------- | --------------------- | | `type` | `typed(json_element_type)` | The type of the value | | `value` | `typed(json_element_value)` | The actual value | ### Element Type + An enum which represents a JSON type + ```C typed(json_element_type) ``` + #### Variants + | **Variant** | **Description** | -|-----------------------------|-----------------| +| --------------------------- | --------------- | | `JSON_ELEMENT_TYPE_STRING` | JSON String | | `JSON_ELEMENT_TYPE_NUMBER` | JSON Number | | `JSON_ELEMENT_TYPE_OBJECT` | JSON Object | @@ -135,13 +166,17 @@ typed(json_element_type) | `JSON_ELEMENT_TYPE_NULL` | JSON Null | ### Element Value + A union for interpreting JSON data + ```C typed(json_element_value) ``` + #### Fields + | **Name** | **Type** | **Interpret data as** | -|--------------|------------------------|-----------------------| +| ------------ | ---------------------- | --------------------- | | `as_string` | `typed(json_string)` | JSON String | | `as_number` | `typed(json_number)` | JSON Number | | `as_object` | `typed(json_object) *` | JSON Object | @@ -149,13 +184,17 @@ typed(json_element_value) | `as_boolean` | `typed(json_boolean)` | JSON Boolean | ### Error + An enum which represents an error + ```C typed(json_error) ``` + #### Variants + | **Variant** | **Description** | -|----------------------------|--------------------------------| +| -------------------------- | ------------------------------ | | `JSON_ERROR_EMPTY` | Null or empty value | | `JSON_ERROR_INVALID_TYPE` | Type inference failed | | `JSON_ERROR_INVALID_KEY` | Key is not a valid string | @@ -173,7 +212,7 @@ const char * some_json_str = "{\"hello\":\"world\",\"key\":\"value\"}"; int main() { result(json_element) element_result = json_parse(some_json_str); - // Guard if + // Guard if if(result_is_err(json_element)(&element_result)) { typed(json_error) error = result_unwrap_err(json_element)(&element_result); fprintf(stderr, "Error parsing JSON: %s\n", json_error_to_string(error)); @@ -184,7 +223,7 @@ int main() { typed(json_element) element = result_unwrap(json_element)(&element_result); // Fetch the "hello" key value - result(json_element) hello_element_result = json_object_find(&element.value.as_object, "hello"); + result(json_element) hello_element_result = json_object_find(element.value.as_object, "hello"); if(result_is_err(json_element)(&hello_element_result)) { typed(json_error) error = result_unwrap_err(json_element)(&hello_element_result); fprintf(stderr, "Error getting element \"hello\": %s\n", json_error_to_string(error)); @@ -200,6 +239,7 @@ int main() { return 0; } ``` + Outputs ``` @@ -212,9 +252,9 @@ Outputs ### Example in repository - 1. Clone this repository `git clone https://github.com/forkachild/C-Simple-JSON-Parser` - 2. Compile the example `clang example.c json.c -o example.out` - 3. Run the binary `./example.out` +1. Clone this repository `git clone https://github.com/forkachild/C-Simple-JSON-Parser` +2. Compile the example `clang example.c json.c -o example.out` +3. Run the binary `./example.out` ## FAQs @@ -298,6 +338,6 @@ for(i = 0; i < arr->count; i++) { ### What if the JSON is poorly formatted with uneven whitespace -Compile using `-DJSON_SCRAPE_WHITESPACE` +Compile using `-DJSON_SKIP_WHITESPACE` ## If this helped you in any way you can [buy me a beer](https://www.paypal.me/suhelchakraborty)