Recently, I needed a json parser for a small C project, and -after googling- found this great library udp / json-parser.
It is great, because it does what it says and it’s really low-footprint.
Soon I realized that I needed two things for my project: a) modifying a string on the json and b) dumping an already parsed json to a file. So, probably json-parser was not the right choice for my project and since I don’t have time to change it now, I decided to implement these functions myself.
Feel free to use, modify or fix as you need.
Disclaimer: note that this code is not 100% ANSI C (specially the json_dump_file function), since my project will only run on Windows you might find crazy things like TCHAR, etc… but it shouldn’t be that difficult to make it ANSI.
/** * Adds a key / value pair to an existing json_object node * @param settings pointer to settings * @param obj the parent object to add the key / value * @param name the key * @param val the value */ void json_add_object_value_ex (json_settings * settings, json_value * obj, json_char * name, json_value * val) { unsigned int i, values_size; json_value *values; if (obj->type != json_object) { // unexpected type! return; } // check if the value already exists for (i = 0; i < obj->u.object.length; i++) { if (!strcmp(obj->u.object.values[i].name, name)) { // in this case just need to change the value json_value_free_ex (settings, obj->u.object.values[i].value); obj->u.object.values[i].value = val; val->parent = obj; return; } } // need to realloc the object values array // increment array length obj->u.object.length++; values_size = obj->u.object.length * sizeof (*obj->u.object.values); values = (json_value *)settings->mem_alloc(values_size, 0, settings->user_data); memcpy(values, obj->u.object.values, (obj->u.object.length - 1) * sizeof (*obj->u.object.values)); *(void **)obj->u.object.values = &values; // set the name and value on the last element of the array obj->u.object.values[obj->u.object.length-1].name = (json_char *) settings->mem_alloc(sizeof(json_char) * strlen(name), 0, settings->user_data); strcpy_s(obj->u.object.values[obj->u.object.length-1].name, sizeof(json_char) * strlen(name), name); obj->u.object.values[obj->u.object.length-1].value = val; val->parent = obj; } /** * Shortcut to json_add_object_value_ex using default memory allocation functions */ void json_add_object_value (json_value * obj, json_char * name, json_value * val) { json_settings settings = { 0 }; memset(&settings, '\0', sizeof(json_settings)); settings.mem_free = default_free; settings.mem_alloc = default_alloc; json_add_object_value_ex (&settings, obj, name, val); } /** * Allocate a json_string * @param settings pointer to settings * @param string the string to allocate * @return a json_value of type json_string with the string on it */ json_value * json_alloc_string_ex (json_settings * settings, json_char * string) { json_value * val; int len = strlen(string); val = (json_value *)settings->mem_alloc(sizeof(json_value), 0, settings->user_data); val->type = json_string; val->u.string.length = len; val->u.string.ptr = (json_char *)settings->mem_alloc(len + 1, 0, settings->user_data); strcpy_s(val->u.string.ptr, len + 1, string); return val; } /** * Shortcut to json_alloc_string_ex using default memory allocation functions */ json_value * json_alloc_string (json_char * string) { json_settings settings = { 0 }; memset(&settings, '\0', sizeof(json_settings)); settings.mem_free = default_free; settings.mem_alloc = default_alloc; return json_alloc_string_ex (&settings, string); } static void json_dump_escaped(FILE *f, char *buf, size_t len) { size_t i; for (i=0; i < len; i++) { switch(*buf) { case '\\': fprintf(f, "%s", "\\\\"); break; case '"': fprintf(f, "%s", "\\\""); break; case '/': fprintf(f, "%s", "\\/"); break; case '\b': fprintf(f, "%s", "\\b"); break; case '\f': fprintf(f, "%s", "\\f"); break; case '\n': fprintf(f, "%s", "\\n"); break; case '\r': fprintf(f, "%s", "\\r"); break; case '\t': fprintf(f, "%s", "\\t"); break; default: fputc(*buf, f); break; } buf++; } } static void json_dump_helper(FILE *f, json_value *v, int prettyprint, unsigned int level) { unsigned int i, j; switch(v->type) { case json_object: fputc('{', f); if (prettyprint) { if (v->u.object.length > 0) fprintf(f, "\n"); } for (i=0; i < v->u.object.length; i++) { for(j=0; j < level; j++) fputc('\t', f); if (prettyprint) fputc('\t', f); fputc('"', f); json_dump_escaped(f, v->u.object.values[i].name, strlen(v->u.object.values[i].name)); fprintf(f, "\":%s", (prettyprint) ? " " : ""); json_dump_helper(f, v->u.object.values[i].value, prettyprint, level + 1); if (i < (v->u.object.length - 1)) { fputc(',', f); if (prettyprint) fputc('\n', f); } } if (prettyprint) { fputc('\n', f); for(j=0; j < level; j++) fputc('\t', f); } fputc('}', f); break; case json_array: fputc('[', f); for (i=0; i < v->u.array.length; i++) { json_dump_helper(f, v->u.array.values[i], prettyprint, level + 1); if (i < (v->u.array.length - 1)) { fputc(',', f); if (prettyprint) fputc(' ', f); } } fputc(']', f); break; case json_integer: fprintf(f, "%lld", v->u.integer); break; case json_double: fprintf(f, "%f", v->u.dbl); break; case json_string: fputc('"', f); json_dump_escaped(f, v->u.string.ptr, v->u.string.length); fputc('"', f); break; case json_boolean: fprintf(f, "%s", (v->u.boolean) ? "true" : "false"); break; case json_null: fprintf(f, "null"); break; } } /** * Dumps a json object into a file */ int json_dump_file(TCHAR *fname, json_value *json, int prettyprint) { FILE *f; errno_t rt = _tfopen_s(&f, fname, _T("wb+")); if (!rt) { json_dump_helper(f, json, prettyprint, 0); fclose(f); return 0; } return 1; }