From c2b004ba0e8a1f9afab2f0c535e1c520124dcc9f Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 29 May 2016 04:54:38 +0200 Subject: [PATCH 1/2] Make default double serializer ignore userdata again The user might want to use the userdata for something different, so the serializer should ignore it by default. Explicitly setting the serializer to json_object_double_to_json_string will still make it interpret the userdata as a format string. --- .gitignore | 1 + json_object.c | 34 +++++++++++++++++++++------ tests/Makefile.am | 3 ++- tests/test_double_serializer.c | 31 ++++++++++++++++++++++++ tests/test_double_serializer.expected | 8 +++++++ tests/test_double_serializer.test | 12 ++++++++++ 6 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 tests/test_double_serializer.c create mode 100644 tests/test_double_serializer.expected create mode 100755 tests/test_double_serializer.test diff --git a/.gitignore b/.gitignore index 57aa6d1..6e0bee2 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ /tests/test_parse /tests/test_cast /tests/test_charcase +/tests/test_double_serializer /tests/test_locale /tests/test_null /tests/test_printbuf diff --git a/json_object.c b/json_object.c index f0d1251..f908540 100644 --- a/json_object.c +++ b/json_object.c @@ -54,6 +54,7 @@ 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_double_to_json_string_default; static json_object_to_json_string_fn json_object_int_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; @@ -261,7 +262,7 @@ void json_object_set_serializer(json_object *jso, 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; + jso->_to_json_string = &json_object_double_to_json_string_default; break; case json_type_int: jso->_to_json_string = &json_object_int_to_json_string; @@ -643,10 +644,11 @@ int64_t json_object_get_int64(const struct json_object *jso) /* json_object_double */ -int json_object_double_to_json_string(struct json_object* jso, - struct printbuf *pb, - int level, - int flags) +static int json_object_double_to_json_string_format(struct json_object* jso, + struct printbuf *pb, + int level, + int flags, + const char *format) { char buf[128], *p, *q; int size; @@ -663,7 +665,7 @@ int json_object_double_to_json_string(struct json_object* jso, size = snprintf(buf, sizeof(buf), "-Infinity"); else size = snprintf(buf, sizeof(buf), - jso->_userdata ? (const char*) jso->_userdata : "%.17g", jso->o.c_double); + format ? format : "%.17g", jso->o.c_double); p = strchr(buf, ','); if (p) { @@ -685,12 +687,30 @@ int json_object_double_to_json_string(struct json_object* jso, return size; } +static int json_object_double_to_json_string_default(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, + NULL); +} + +int json_object_double_to_json_string(struct json_object* jso, + struct printbuf *pb, + int level, + int flags) +{ + return json_object_double_to_json_string_format(jso, pb, level, flags, + jso->_userdata); +} + struct json_object* json_object_new_double(double d) { struct json_object *jso = json_object_new(json_type_double); if (!jso) return NULL; - jso->_to_json_string = &json_object_double_to_json_string; + jso->_to_json_string = &json_object_double_to_json_string_default; jso->o.c_double = d; return jso; } diff --git a/tests/Makefile.am b/tests/Makefile.am index 79b196b..7fdb2d9 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -11,6 +11,7 @@ TESTS+= testReplaceExisting.test TESTS+= test_parse_int64.test TESTS+= test_null.test TESTS+= test_cast.test +TESTS+= test_double_serializer.test TESTS+= test_parse.test TESTS+= test_locale.test TESTS+= test_charcase.test @@ -22,7 +23,7 @@ check_PROGRAMS= check_PROGRAMS += $(TESTS:.test=) # Note: handled by test1.test -check_PROGRAMS += test1Formatted +check_PROGRAMS += test1Formatted test1Formatted_SOURCES = test1.c parse_flags.c test1Formatted_CPPFLAGS = -DTEST_FORMATTED diff --git a/tests/test_double_serializer.c b/tests/test_double_serializer.c new file mode 100644 index 0000000..4ef754d --- /dev/null +++ b/tests/test_double_serializer.c @@ -0,0 +1,31 @@ +/* +* Tests if the format string for double serialization is handled correctly +*/ + +#include +#include "config.h" + +#include "json_object.h" +#include "json_object_private.h" + +int main() +{ + struct json_object *obj = json_object_new_double(0.5); + + printf("Test default serializer:\n"); + printf("obj.to_string(standard)=%s\n", json_object_to_json_string(obj)); + + printf("Test default serializer with custom userdata:\n"); + obj->_userdata = "test"; + printf("obj.to_string(userdata)=%s\n", json_object_to_json_string(obj)); + + printf("Test explicit serializer with custom userdata:\n"); + json_object_set_serializer(obj, json_object_double_to_json_string, "test", NULL); + printf("obj.to_string(custom)=%s\n", json_object_to_json_string(obj)); + + printf("Test reset serializer:\n"); + json_object_set_serializer(obj, NULL, NULL, NULL); + printf("obj.to_string(reset)=%s\n", json_object_to_json_string(obj)); + + json_object_put(obj); +} diff --git a/tests/test_double_serializer.expected b/tests/test_double_serializer.expected new file mode 100644 index 0000000..da645d7 --- /dev/null +++ b/tests/test_double_serializer.expected @@ -0,0 +1,8 @@ +Test default serializer: +obj.to_string(standard)=0.5 +Test default serializer with custom userdata: +obj.to_string(userdata)=0.5 +Test explicit serializer with custom userdata: +obj.to_string(custom)=test +Test reset serializer: +obj.to_string(reset)=0.5 diff --git a/tests/test_double_serializer.test b/tests/test_double_serializer.test new file mode 100755 index 0000000..d4abac4 --- /dev/null +++ b/tests/test_double_serializer.test @@ -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_double_serializer +exit $? From f87e378d481fb13e8d21cec0585e88a297a57559 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sun, 29 May 2016 05:54:57 +0200 Subject: [PATCH 2/2] Add public API to get and set userdata Also, json_object_set_serializer is changed to respect the userdata and user_delete parameters when to_string_func is NULL. --- json_object.c | 25 ++++++++++++-------- json_object.h | 63 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 18 deletions(-) diff --git a/json_object.c b/json_object.c index f908540..93aefd5 100644 --- a/json_object.c +++ b/json_object.c @@ -235,6 +235,21 @@ enum json_type json_object_get_type(const struct json_object *jso) return jso->o_type; } +void* json_object_get_userdata(json_object *jso) { + return jso->_userdata; +} + +void json_object_set_userdata(json_object *jso, 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 = userdata; + jso->_user_delete = user_delete; +} + /* set a custom conversion to string */ void json_object_set_serializer(json_object *jso, @@ -242,13 +257,7 @@ void json_object_set_serializer(json_object *jso, 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; + json_object_set_userdata(jso, userdata, user_delete); if (to_string_func == NULL) { @@ -281,8 +290,6 @@ void json_object_set_serializer(json_object *jso, } jso->_to_json_string = to_string_func; - jso->_userdata = userdata; - jso->_user_delete = user_delete; } diff --git a/json_object.h b/json_object.h index d8090f8..29af8e9 100644 --- a/json_object.h +++ b/json_object.h @@ -222,19 +222,55 @@ 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); +/** + * Returns the userdata set by json_object_set_userdata() or + * json_object_set_serializer() + * + * @param jso the object to return the userdata for + */ +extern void* json_object_get_userdata(json_object *jso); + +/** + * Set an opaque userdata value for an object + * + * The userdata can be retrieved using json_object_get_userdata(). + * + * If custom userdata is already set on this object, any existing user_delete + * function is called before the new one is set. + * + * 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) + * + * Note: Objects created by parsing strings may have custom serializers set + * which expect the userdata to contain specific data (due to use of + * json_object_new_double_s()). In this case, json_object_set_serialiser() with + * NULL as to_string_func should be used instead to set the userdata and reset + * the serializer to its default value. + * + * @param jso the object to set the userdata for + * @param userdata an optional opaque cookie + * @param user_delete an optional function from freeing userdata + */ +extern void json_object_set_userdata(json_object *jso, void *userdata, + json_object_delete_fn *user_delete); + /** * 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 custom userdata 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. + * If to_string_func is NULL the default behaviour is reset (but the userdata + * and user_delete fields are still set). * - * 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 userdata parameter is optional and may be passed as NULL. It can be used + * to provide additional data for to_string_func to use. 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 @@ -243,6 +279,10 @@ flags); * 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) * + * Note that the userdata is the same as set by json_object_set_userdata(), so + * care must be taken not to overwrite the value when both a custom serializer + * and json_object_set_userdata() are used. + * * @param jso the object to customize * @param to_string_func the custom serialization function * @param userdata an optional opaque cookie @@ -634,8 +674,13 @@ extern struct json_object* json_object_new_double(double d); * inefficiently (e.g. 12.3 => "12.300000000000001") to be * serialized with the more convenient form. * - * Note: this is used by json_tokener_parse_ex() to allow for - * an exact re-serialization of a parsed object. + * Notes: + * + * This is used by json_tokener_parse_ex() to allow for + * an exact re-serialization of a parsed object. + * + * The userdata field is used to store the string representation, so it + * can't be used for other data if this function is used. * * An equivalent sequence of calls is: * @code