Skip to content

Commit

Permalink
Add a json_set_serializer() function to allow the string output of a …
Browse files Browse the repository at this point in the history
…json_object to be customized.
  • Loading branch information
hawicz committed Sep 2, 2012
1 parent f74e8f8 commit 38f421a
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
/tests/test_cast
/tests/test_null
/tests/test_printbuf
/tests/test_set_serializer
/Debug
/Release
*.lo
Expand Down
72 changes: 68 additions & 4 deletions json_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ const char *json_hex_chars = "0123456789abcdefABCDEF";
static void json_object_generic_delete(struct json_object* jso);
static struct json_object* json_object_new(enum json_type o_type);

static json_object_to_json_string_fn json_object_object_to_json_string;
static json_object_to_json_string_fn json_object_boolean_to_json_string;
static json_object_to_json_string_fn json_object_int_to_json_string;
static json_object_to_json_string_fn json_object_double_to_json_string;
static json_object_to_json_string_fn json_object_string_to_json_string;
static json_object_to_json_string_fn json_object_array_to_json_string;


/* ref count debugging */

Expand Down Expand Up @@ -134,10 +141,16 @@ extern struct json_object* json_object_get(struct json_object *jso)

extern void json_object_put(struct json_object *jso)
{
if(jso) {
jso->_ref_count--;
if(!jso->_ref_count) jso->_delete(jso);
}
if(jso)
{
jso->_ref_count--;
if(!jso->_ref_count)
{
if (jso->_user_delete)
jso->_user_delete(jso, jso->_userdata);
jso->_delete(jso);
}
}
}


Expand Down Expand Up @@ -187,6 +200,57 @@ enum json_type json_object_get_type(struct json_object *jso)
return jso->o_type;
}

/* set a custom conversion to string */

void json_object_set_serializer(json_object *jso,
json_object_to_json_string_fn to_string_func,
void *userdata,
json_object_delete_fn *user_delete)
{
// First, clean up any previously existing user info
if (jso->_user_delete)
{
jso->_user_delete(jso, jso->_userdata);
}
jso->_userdata = NULL;
jso->_user_delete = NULL;

if (to_string_func == NULL)
{
// Reset to the standard serialization function
switch(jso->o_type)
{
case json_type_null:
jso->_to_json_string = NULL;
break;
case json_type_boolean:
jso->_to_json_string = &json_object_boolean_to_json_string;
break;
case json_type_double:
jso->_to_json_string = &json_object_double_to_json_string;
break;
case json_type_int:
jso->_to_json_string = &json_object_int_to_json_string;
break;
case json_type_object:
jso->_to_json_string = &json_object_object_to_json_string;
break;
case json_type_array:
jso->_to_json_string = &json_object_array_to_json_string;
break;
case json_type_string:
jso->_to_json_string = &json_object_string_to_json_string;
break;
}
return;
}

jso->_to_json_string = to_string_func;
jso->_userdata = userdata;
jso->_user_delete = user_delete;
}


/* extended conversion to string */

const char* json_object_to_json_string_ext(struct json_object *jso, int flags)
Expand Down
45 changes: 45 additions & 0 deletions json_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ typedef struct json_object json_object;
typedef struct json_object_iter json_object_iter;
typedef struct json_tokener json_tokener;

/**
* Type of custom user delete functions. See json_object_set_serializer.
*/
typedef void (json_object_delete_fn)(struct json_object *jso, void *userdata);

/**
* Type of a custom serialization function. See json_object_set_serializer.
*/
typedef int (json_object_to_json_string_fn)(struct json_object *jso,
struct printbuf *pb,
int level,
int flags);

/* supported object types */

typedef enum json_type {
Expand Down Expand Up @@ -149,6 +162,38 @@ extern const char* json_object_to_json_string(struct json_object *obj);
extern const char* json_object_to_json_string_ext(struct json_object *obj, int
flags);

/**
* Set a custom serialization function to be used when this particular object
* is converted to a string by json_object_to_json_string.
*
* If a custom serializer is already set on this object, any existing
* user_delete function is called before the new one is set.
*
* If to_string_func is NULL, the other parameters are ignored
* and the default behaviour is reset.
*
* The userdata parameter is optional and may be passed as NULL. If provided,
* it is passed to to_string_func as-is. This parameter may be NULL even
* if user_delete is non-NULL.
*
* The user_delete parameter is optional and may be passed as NULL, even if
* the userdata parameter is non-NULL. It will be called just before the
* json_object is deleted, after it's reference count goes to zero
* (see json_object_put()).
* If this is not provided, it is up to the caller to free the userdata at
* an appropriate time. (i.e. after the json_object is deleted)
*
* @param jso the object to customize
* @param to_string_func the custom serialization function
* @param userdata an optional opaque cookie
* @param user_delete an optional function from freeing userdata
*/
void json_object_set_serializer(json_object *jso,
json_object_to_json_string_fn to_string_func,
void *userdata,
json_object_delete_fn *user_delete);



/* object type methods */

Expand Down
10 changes: 4 additions & 6 deletions json_object_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,12 @@
extern "C" {
#endif

typedef void (json_object_delete_fn)(struct json_object *o);
typedef int (json_object_to_json_string_fn)(struct json_object *o,
struct printbuf *pb,
int level,
int flags);
typedef void (json_object_private_delete_fn)(struct json_object *o);

struct json_object
{
enum json_type o_type;
json_object_delete_fn *_delete;
json_object_private_delete_fn *_delete;
json_object_to_json_string_fn *_to_json_string;
int _ref_count;
struct printbuf *_pb;
Expand All @@ -40,6 +36,8 @@ struct json_object
int len;
} c_string;
} o;
json_object_delete_fn *_user_delete;
void *_userdata;
};

#ifdef __cplusplus
Expand Down
4 changes: 4 additions & 0 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ TESTS+= test_printbuf.test
check_PROGRAMS+=test_printbuf
test_printbuf_LDADD = $(LIBJSON_LA)

TESTS+= test_set_serializer.test
check_PROGRAMS += test_set_serializer
test_set_serializer_LDADD = $(LIBJSON_LA)

EXTRA_DIST=
EXTRA_DIST += $(TESTS)

Expand Down
71 changes: 71 additions & 0 deletions tests/test_set_serializer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include <assert.h>
#include <stdio.h>
#include <string.h>

#include "json.h"
#include "printbuf.h"

struct myinfo {
int value;
};

static int freeit_was_called = 0;
static void freeit(json_object *jso, void *userdata)
{
struct myinfo *info = userdata;
printf("freeit, value=%d\n", info->value);
// Don't actually free anything here, the userdata is stack allocated.
freeit_was_called = 1;
}
static int custom_serializer(struct json_object *o,
struct printbuf *pb,
int level,
int flags)
{
sprintbuf(pb, "Custom Output");
return 0;
}

int main(int argc, char **argv)
{
json_object *my_object;

MC_SET_DEBUG(1);

printf("Test setting, then resetting a custom serializer:\n");
my_object = json_object_new_object();
json_object_object_add(my_object, "abc", json_object_new_int(12));
json_object_object_add(my_object, "foo", json_object_new_string("bar"));

printf("my_object.to_string(standard)=%s\n", json_object_to_json_string(my_object));

struct myinfo userdata = { .value = 123 };
json_object_set_serializer(my_object, custom_serializer, &userdata, freeit);

printf("my_object.to_string(custom serializer)=%s\n", json_object_to_json_string(my_object));

printf("Next line of output should be from the custom freeit function:\n");
freeit_was_called = 0;
json_object_set_serializer(my_object, NULL, NULL, NULL);
assert(freeit_was_called);

printf("my_object.to_string(standard)=%s\n", json_object_to_json_string(my_object));

json_object_put(my_object);

// ============================================

my_object = json_object_new_object();
printf("Check that the custom serializer isn't free'd until the last json_object_put:\n");
json_object_set_serializer(my_object, custom_serializer, &userdata, freeit);
json_object_get(my_object);
json_object_put(my_object);
printf("my_object.to_string(custom serializer)=%s\n", json_object_to_json_string(my_object));
printf("Next line of output should be from the custom freeit function:\n");

freeit_was_called = 0;
json_object_put(my_object);
assert(freeit_was_called);

return 0;
}
9 changes: 9 additions & 0 deletions tests/test_set_serializer.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
my_object.to_string(standard)={ "abc": 12, "foo": "bar" }
my_object.to_string(custom serializer)=Custom Output
Next line of output should be from the custom freeit function:
freeit, value=123
my_object.to_string(standard)={ "abc": 12, "foo": "bar" }
Check that the custom serializer isn't free'd until the last json_object_put:
my_object.to_string(custom serializer)=Custom Output
Next line of output should be from the custom freeit function:
freeit, value=123
12 changes: 12 additions & 0 deletions tests/test_set_serializer.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh

# Common definitions
if test -z "$srcdir"; then
srcdir="${0%/*}"
test "$srcdir" = "$0" && srcdir=.
test -z "$srcdir" && srcdir=.
fi
. "$srcdir/test-defs.sh"

run_output_test test_set_serializer
exit $?

0 comments on commit 38f421a

Please sign in to comment.