Utility functions for udp / json-parser
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;
}
Comments