| @@ -165,6 +165,9 @@ JSONC_0.15 { | |||||
| } JSONC_0.14; | } JSONC_0.14; | ||||
| JSONC_0.16 { | JSONC_0.16 { | ||||
| # global: | |||||
| # ...new symbols here... | |||||
| global: | |||||
| json_object_object_add_len; | |||||
| json_object_object_add_ex_len; | |||||
| json_object_object_get_len; | |||||
| json_object_object_get_ex_len; | |||||
| } JSONC_0.15; | } JSONC_0.15; | ||||
| @@ -582,6 +582,12 @@ struct lh_table *json_object_get_object(const struct json_object *jso) | |||||
| int json_object_object_add_ex(struct json_object *jso, const char *const key, | int json_object_object_add_ex(struct json_object *jso, const char *const key, | ||||
| struct json_object *const val, const unsigned opts) | struct json_object *const val, const unsigned opts) | ||||
| { | |||||
| return json_object_object_add_ex_len(jso, key, strlen(key), val, opts); | |||||
| } | |||||
| int json_object_object_add_ex_len(struct json_object *jso, const char *const key, const int len, | |||||
| struct json_object *const val, const unsigned opts) | |||||
| { | { | ||||
| struct json_object *existing_value = NULL; | struct json_object *existing_value = NULL; | ||||
| struct lh_entry *existing_entry; | struct lh_entry *existing_entry; | ||||
| @@ -619,7 +625,13 @@ int json_object_object_add_ex(struct json_object *jso, const char *const key, | |||||
| int json_object_object_add(struct json_object *jso, const char *key, struct json_object *val) | int json_object_object_add(struct json_object *jso, const char *key, struct json_object *val) | ||||
| { | { | ||||
| return json_object_object_add_ex(jso, key, val, 0); | |||||
| return json_object_object_add_ex_len(jso, key, strlen(key), val, 0); | |||||
| } | |||||
| int json_object_object_add_len(struct json_object *jso, const char *key, const int len, | |||||
| struct json_object *val) | |||||
| { | |||||
| return json_object_object_add_ex_len(jso, key, len, val, 0); | |||||
| } | } | ||||
| int json_object_object_length(const struct json_object *jso) | int json_object_object_length(const struct json_object *jso) | ||||
| @@ -636,12 +648,26 @@ size_t json_c_object_sizeof(void) | |||||
| struct json_object *json_object_object_get(const struct json_object *jso, const char *key) | struct json_object *json_object_object_get(const struct json_object *jso, const char *key) | ||||
| { | { | ||||
| struct json_object *result = NULL; | struct json_object *result = NULL; | ||||
| json_object_object_get_ex(jso, key, &result); | |||||
| json_object_object_get_ex_len(jso, key, strlen(key), &result); | |||||
| return result; | |||||
| } | |||||
| struct json_object *json_object_object_get_len(const struct json_object *jso, const char *key, | |||||
| const int len) | |||||
| { | |||||
| struct json_object *result = NULL; | |||||
| json_object_object_get_ex_len(jso, key, len, &result); | |||||
| return result; | return result; | ||||
| } | } | ||||
| json_bool json_object_object_get_ex(const struct json_object *jso, const char *key, | json_bool json_object_object_get_ex(const struct json_object *jso, const char *key, | ||||
| struct json_object **value) | struct json_object **value) | ||||
| { | |||||
| return json_object_object_get_ex_len(jso, key, strlen(key), value); | |||||
| } | |||||
| json_bool json_object_object_get_ex_len(const struct json_object *jso, const char *key, | |||||
| const int len, struct json_object **value) | |||||
| { | { | ||||
| if (value != NULL) | if (value != NULL) | ||||
| *value = NULL; | *value = NULL; | ||||
| @@ -385,6 +385,36 @@ JSON_C_CONST_FUNCTION(JSON_EXPORT size_t json_c_object_sizeof(void)); | |||||
| JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key, | JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key, | ||||
| struct json_object *val); | struct json_object *val); | ||||
| /** Add an object field to a json_object of type json_type_object | |||||
| * | |||||
| * The reference count of `val` will *not* be incremented, in effect | |||||
| * transferring ownership that object to `obj`, and thus `val` will be | |||||
| * freed when `obj` is. (i.e. through `json_object_put(obj)`) | |||||
| * | |||||
| * If you want to retain a reference to the added object, independent | |||||
| * of the lifetime of obj, you must increment the refcount with | |||||
| * `json_object_get(val)` (and later release it with json_object_put()). | |||||
| * | |||||
| * Since ownership transfers to `obj`, you must make sure | |||||
| * that you do in fact have ownership over `val`. For instance, | |||||
| * json_object_new_object() will give you ownership until you transfer it, | |||||
| * whereas json_object_object_get() does not. | |||||
| * | |||||
| * Any previous object stored under `key` in `obj` will have its refcount | |||||
| * decremented, and be freed normally if that drops to zero. | |||||
| * | |||||
| * @param obj the json_object instance | |||||
| * @param key the object field name (a private copy will be duplicated), | |||||
| * which is not terminated by a NULL ( @c '\0' ) character | |||||
| * @param len the length of @p key | |||||
| * @param val a json_object or NULL member to associate with the given field | |||||
| * | |||||
| * @return On success, @c 0 is returned. | |||||
| * On error, a negative value is returned. | |||||
| */ | |||||
| JSON_EXPORT int json_object_object_add_len(struct json_object *obj, const char *key, const int len, | |||||
| struct json_object *val); | |||||
| /** Add an object field to a json_object of type json_type_object | /** Add an object field to a json_object of type json_type_object | ||||
| * | * | ||||
| * The semantics are identical to json_object_object_add, except that an | * The semantics are identical to json_object_object_add, except that an | ||||
| @@ -397,13 +427,51 @@ JSON_EXPORT int json_object_object_add(struct json_object *obj, const char *key, | |||||
| * @param val a json_object or NULL member to associate with the given field | * @param val a json_object or NULL member to associate with the given field | ||||
| * @param opts process-modifying options. To specify multiple options, use | * @param opts process-modifying options. To specify multiple options, use | ||||
| * (OPT1|OPT2) | * (OPT1|OPT2) | ||||
| * | |||||
| * @return On success, @c 0 is returned. | |||||
| * On error, a negative value is returned. | |||||
| */ | */ | ||||
| JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *const key, | JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *const key, | ||||
| struct json_object *const val, const unsigned opts); | struct json_object *const val, const unsigned opts); | ||||
| /** Add an object field to a json_object of type json_type_object | |||||
| * | |||||
| * The semantics are identical to json_object_object_add, except that an | |||||
| * additional flag fields gives you more control over some detail aspects | |||||
| * of processing. See the description of JSON_C_OBJECT_ADD_* flags for more | |||||
| * details. | |||||
| * | |||||
| * @param obj the json_object instance | |||||
| * @param key the object field name (a private copy will be duplicated), | |||||
| * which is not terminated by a NULL ( @c '\0' ) character | |||||
| * @param len the length of @p key | |||||
| * @param val a json_object or NULL member to associate with the given field | |||||
| * @param opts process-modifying options. To specify multiple options, use | |||||
| * (OPT1|OPT2) | |||||
| * | |||||
| * @return On success, @c 0 is returned. | |||||
| * On error, a negative value is returned. | |||||
| */ | |||||
| JSON_EXPORT int json_object_object_add_ex_len(struct json_object *obj, const char *const key, | |||||
| const int len, struct json_object *const val, | |||||
| const unsigned opts); | |||||
| /** Get the json_object associate with a given object field. | /** Get the json_object associate with a given object field. | ||||
| * Deprecated/discouraged: used json_object_object_get_ex instead. | * Deprecated/discouraged: used json_object_object_get_ex instead. | ||||
| * | * | ||||
| * This functions exactly like calling | |||||
| * @code json_object_object_get_len(obj, key, strlen(key)) @endcode | |||||
| * | |||||
| * @param obj the json_object instance | |||||
| * @param key the object field name | |||||
| * @returns the json_object associated with the given field name | |||||
| * @see json_object_object_get_len() | |||||
| */ | |||||
| JSON_EXPORT struct json_object *json_object_object_get(const struct json_object *obj, | |||||
| const char *key); | |||||
| /** Get the json_object associate with a given object field. | |||||
| * Deprecated/discouraged: used json_object_object_get_ex_len instead. | |||||
| * | |||||
| * This returns NULL if the field is found but its value is null, or if | * This returns NULL if the field is found but its value is null, or if | ||||
| * the field is not found, or if obj is not a json_type_object. If you | * the field is not found, or if obj is not a json_type_object. If you | ||||
| * need to distinguish between these cases, use json_object_object_get_ex(). | * need to distinguish between these cases, use json_object_object_get_ex(). | ||||
| @@ -419,11 +487,31 @@ JSON_EXPORT int json_object_object_add_ex(struct json_object *obj, const char *c | |||||
| * or transfer ownership to prevent a memory leak). | * or transfer ownership to prevent a memory leak). | ||||
| * | * | ||||
| * @param obj the json_object instance | * @param obj the json_object instance | ||||
| * @param key the object field name | |||||
| * @param key the object field name, | |||||
| * which is not terminated by a NULL ( @c '\0' ) character | |||||
| * @param len the length of @p key | |||||
| * @returns the json_object associated with the given field name | * @returns the json_object associated with the given field name | ||||
| */ | */ | ||||
| JSON_EXPORT struct json_object *json_object_object_get(const struct json_object *obj, | |||||
| const char *key); | |||||
| JSON_EXPORT struct json_object *json_object_object_get_len(const struct json_object *obj, | |||||
| const char *key, const int len); | |||||
| /** Get the json_object associated with a given object field. | |||||
| * | |||||
| * This functions exactly like calling | |||||
| * @code json_object_object_get_ex_len(obj, key, strlen(key), value) @endcode | |||||
| * | |||||
| * @param obj the json_object instance | |||||
| * @param key the object field name | |||||
| * @param value a pointer where to store a reference to the json_object | |||||
| * associated with the given field name. | |||||
| * \n | |||||
| * It is safe to pass a NULL value. | |||||
| * @returns whether or not the key exists | |||||
| * @see json_object_object_get_ex_len() | |||||
| */ | |||||
| JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object *obj, const char *key, | |||||
| struct json_object **value); | |||||
| /** Get the json_object associated with a given object field. | /** Get the json_object associated with a given object field. | ||||
| * | * | ||||
| @@ -436,15 +524,17 @@ JSON_EXPORT struct json_object *json_object_object_get(const struct json_object | |||||
| * than the owning parent (obj). Ownership of value is retained by obj. | * than the owning parent (obj). Ownership of value is retained by obj. | ||||
| * | * | ||||
| * @param obj the json_object instance | * @param obj the json_object instance | ||||
| * @param key the object field name | |||||
| * @param key the object field name, | |||||
| * which is not terminated by a NULL ( @c '\0' ) character | |||||
| * @param len the length of @p key | |||||
| * @param value a pointer where to store a reference to the json_object | * @param value a pointer where to store a reference to the json_object | ||||
| * associated with the given field name. | * associated with the given field name. | ||||
| * | |||||
| * \n | |||||
| * It is safe to pass a NULL value. | * It is safe to pass a NULL value. | ||||
| * @returns whether or not the key exists | * @returns whether or not the key exists | ||||
| */ | */ | ||||
| JSON_EXPORT json_bool json_object_object_get_ex(const struct json_object *obj, const char *key, | |||||
| struct json_object **value); | |||||
| JSON_EXPORT json_bool json_object_object_get_ex_len(const struct json_object *obj, const char *key, | |||||
| const int len, struct json_object **value); | |||||
| /** Delete the given json_object field | /** Delete the given json_object field | ||||
| * | * | ||||
| @@ -26,6 +26,8 @@ set(ALL_TEST_NAMES | |||||
| test_int_add | test_int_add | ||||
| test_locale | test_locale | ||||
| test_null | test_null | ||||
| test_null_keys_add | |||||
| test_null_keys_get | |||||
| test_parse | test_parse | ||||
| test_parse_int64 | test_parse_int64 | ||||
| test_printbuf | test_printbuf | ||||
| @@ -0,0 +1,123 @@ | |||||
| /* | |||||
| * Tests if binary strings are supported. | |||||
| */ | |||||
| #include "config.h" | |||||
| #include <stdio.h> | |||||
| #include <string.h> | |||||
| #include "json_inttypes.h" | |||||
| #include "json_object.h" | |||||
| #include "json_tokener.h" | |||||
| int main(void) | |||||
| { | |||||
| /* this test has embedded null characters in the key and value. | |||||
| * check that it's still included after deserializing. */ | |||||
| const char *input = "{ \"foo\": 14.5, \"bar\": [] }"; | |||||
| const char *foo_key = "foo"; | |||||
| const char *foo_value = "14.5"; | |||||
| const char *bar_key = "bar"; | |||||
| const char *toadd_key = "foo\0bar"; | |||||
| const int toadd_key_len = 7; | |||||
| const char *toadd_key_printable = "foo\\0bar"; | |||||
| const char *toadd_value = "qwerty\0asdf"; | |||||
| const int toadd_value_len = 12; | |||||
| const char *toadd_value_printable = "qwerty\\0asdf"; | |||||
| struct json_object *parsed = json_tokener_parse(input); | |||||
| printf("Parsed input: %s\n", input); | |||||
| printf("Result is "); | |||||
| if (parsed == NULL) | |||||
| { | |||||
| printf("NULL (error!)\n"); | |||||
| return 1; // Error somewhere | |||||
| } | |||||
| else if (!json_object_is_type(parsed, json_type_object)) | |||||
| { | |||||
| printf("not `json_type_object` (error!)\n"); | |||||
| return 1; // Error somewhere | |||||
| } | |||||
| else | |||||
| { | |||||
| printf("`json_type_object`\n"); | |||||
| } | |||||
| // Check nothing odd happened in parsing | |||||
| if (json_object_object_length(parsed) != 2) | |||||
| { | |||||
| printf("Should contain two fields (has %d) (error!)", | |||||
| json_object_object_length(parsed)); | |||||
| return 1; | |||||
| } | |||||
| json_bool key_present = json_object_object_get_ex(parsed, foo_key, NULL); | |||||
| if (!key_present) | |||||
| { | |||||
| printf("Should contain key \"%s\", but does not (error!)", foo_key); | |||||
| return 1; | |||||
| } | |||||
| key_present = json_object_object_get_ex(parsed, bar_key, NULL); | |||||
| if (!key_present) | |||||
| { | |||||
| printf("Should contain key \"%s\", but does not (error!)", bar_key); | |||||
| return 1; | |||||
| } | |||||
| // Add the new key | |||||
| struct json_object *new_str = json_object_new_string_len(toadd_value, toadd_value_len); | |||||
| if (json_object_object_add_ex(parsed, toadd_key, new_str, | |||||
| JSON_C_OBJECT_ADD_KEY_IS_NEW | | |||||
| JSON_C_OBJECT_KEY_IS_CONSTANT) != 0) | |||||
| { | |||||
| printf("An error occured adding the key \"%s\" (error!)\n", toadd_key_printable); | |||||
| return 1; | |||||
| } | |||||
| // Check the new key was actually added | |||||
| if (json_object_object_length(parsed) != 3) | |||||
| { | |||||
| printf("Should contain three fields after adding new key (has %d) (error!)", | |||||
| json_object_object_length(parsed)); | |||||
| return 1; | |||||
| } | |||||
| else if (json_object_object_get_len(parsed, toadd_key, toadd_key_len) != new_str) | |||||
| { | |||||
| printf("Have three keys, but don't have the right value in \"%s\" (error!)\n", | |||||
| toadd_key_printable); | |||||
| return 1; | |||||
| } | |||||
| else | |||||
| { | |||||
| printf("Added the key \"%s\" successfully\n", toadd_key_printable); | |||||
| } | |||||
| // Check the previous keys are still the same present | |||||
| struct json_object *foo = json_object_object_get(parsed, foo_key); | |||||
| if (!json_object_is_type(foo, json_type_double)) | |||||
| { | |||||
| printf("Key \"%s\" should be `json_type_double` (%d) but was %d (error!)\n", | |||||
| foo_key, (int)json_type_double, json_object_get_type(foo)); | |||||
| return 1; | |||||
| } | |||||
| else | |||||
| { | |||||
| printf("Key \"%s\" is still the same\n", foo_key); | |||||
| } | |||||
| struct json_object *bar = json_object_object_get(parsed, bar_key); | |||||
| if (!json_object_is_type(foo, json_type_array)) | |||||
| { | |||||
| printf("Key \"%s\" should be `json_type_array` (%d) but was %d (error!)\n", bar_key, | |||||
| (int)json_type_array, json_object_get_type(foo)); | |||||
| return 1; | |||||
| } | |||||
| else | |||||
| { | |||||
| printf("Key \"%s\" is still the same\n", bar_key); | |||||
| } | |||||
| json_object_put(parsed); | |||||
| printf("PASS\n"); | |||||
| return 0; | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| Parsed input: { "foo": 14.5, "bar": [] } | |||||
| Result is `json_type_object` | |||||
| Added the key "foo\0bar" successfully | |||||
| Key "foo" is still the same | |||||
| Key "bar" is still the same | |||||
| PASS | |||||
| @@ -0,0 +1 @@ | |||||
| test_basic.test | |||||
| @@ -0,0 +1,139 @@ | |||||
| /* | |||||
| * Tests if binary strings are supported. | |||||
| */ | |||||
| #include "config.h" | |||||
| #include <stdio.h> | |||||
| #include <string.h> | |||||
| #include "json_inttypes.h" | |||||
| #include "json_object.h" | |||||
| #include "json_tokener.h" | |||||
| int main(void) | |||||
| { | |||||
| /* this test has embedded null characters in the key and value. | |||||
| * check that it's still included after deserializing. */ | |||||
| const char *input = "{ \"foo\\u0000bar\": \"qwerty\\u0000asdf\" }"; | |||||
| const char *expected_key = "foo\0bar"; | |||||
| const int expected_key_len = 7; | |||||
| const char *expected_key_printable = "foo\\0bar"; | |||||
| const char *expected_value = "qwerty\0asdf"; | |||||
| const int expected_value_len = 12; | |||||
| const char *expected_value_printable = "qwerty\\0asdf"; | |||||
| struct json_object *parsed = json_tokener_parse(input); | |||||
| printf("Parsed input: %s\n", input); | |||||
| printf("Result is "); | |||||
| if (parsed == NULL) | |||||
| { | |||||
| printf("NULL (error!)\n"); | |||||
| return 1; // Error somewhere | |||||
| } | |||||
| else if (!json_object_is_type(parsed, json_type_object)) | |||||
| { | |||||
| printf("not `json_type_object` (error!)\n"); | |||||
| return 1; // Error somewhere | |||||
| } | |||||
| else | |||||
| { | |||||
| printf("`json_type_object`\n"); | |||||
| } | |||||
| // Check nothing odd happened in parsing | |||||
| if (json_object_object_length(parsed) != 1) | |||||
| { | |||||
| printf("Should contain only one field (has %d) (error!)", | |||||
| json_object_object_length(parsed)); | |||||
| return 1; | |||||
| } | |||||
| json_bool key_present = json_object_object_get_ex(parsed, expected_key, NULL); | |||||
| if (key_present) | |||||
| { | |||||
| printf("The key \"%s\" should not be present when calling " | |||||
| "`json_object_object_get_ex` " | |||||
| "(the real key contains a NUL character). (error!)\n", | |||||
| expected_key); | |||||
| return 1; | |||||
| } | |||||
| // Check the key is present | |||||
| struct json_object *string; | |||||
| key_present = json_object_object_get_ex_len(parsed, expected_key, 7, &string); | |||||
| if (!key_present) | |||||
| { | |||||
| printf("The key \"%s\" should be present when calling " | |||||
| "`json_object_object_get_ex_len` (error!)\n", | |||||
| expected_key_printable); | |||||
| return 1; | |||||
| } | |||||
| else if (string == NULL) | |||||
| { | |||||
| printf("The key \"%s\" was present when calling " | |||||
| "`json_object_object_get_ex_len`, but got NULL (error!)\n", | |||||
| expected_key_printable); | |||||
| return 1; | |||||
| } | |||||
| struct json_object *from_get_len = json_object_object_get_len(parsed, expected_key, 7); | |||||
| if (string != from_get_len) | |||||
| { | |||||
| printf("The value returned from `json_object_object_get_len` should be the " | |||||
| "same as the one from `json_object_object_get_ex_len` (error!)\n" | |||||
| "Got %p from `json_object_object_get_ex_len` but %p from " | |||||
| "`json_object_object_get_len`\n", | |||||
| string, from_get_len); | |||||
| return 1; | |||||
| } | |||||
| else | |||||
| { | |||||
| printf("Key was present and same for " | |||||
| "`json_object_object_get_ex_len` and `json_object_object_get_len`\n"); | |||||
| } | |||||
| // Check the value is right | |||||
| if (!json_object_is_type(string, json_type_string)) | |||||
| { | |||||
| printf( | |||||
| "Value is wrong type, expected `json_type_string` (%d) but got %d (error!)\n", | |||||
| json_type_string, json_object_get_type(string)); | |||||
| return 1; | |||||
| } | |||||
| else | |||||
| { | |||||
| printf("Value is right type, `json_type_string`\n"); | |||||
| } | |||||
| const int actual_value_len = json_object_get_string_len(string); | |||||
| const char *actual_value = json_object_get_string(string); | |||||
| if (actual_value_len != expected_value_len) | |||||
| { | |||||
| printf("Value is wrong length, expected %d but got %d (error!)\n", | |||||
| expected_value_len, actual_value_len); | |||||
| return 1; | |||||
| } | |||||
| else if (memcmp(expected_value, actual_value, actual_value_len) != 0) | |||||
| { | |||||
| printf("Expected \"%s\" but got \"", expected_value_printable); | |||||
| for (int i = 0; i < actual_value_len; ++i) | |||||
| { | |||||
| if (actual_value[i] == '\0') | |||||
| { | |||||
| puts("\\0"); | |||||
| } | |||||
| else | |||||
| { | |||||
| putchar(actual_value[i]); | |||||
| } | |||||
| } | |||||
| printf("\"\n"); | |||||
| return 1; | |||||
| } | |||||
| else | |||||
| { | |||||
| printf("Expected value matches actual\n"); | |||||
| } | |||||
| json_object_put(parsed); | |||||
| printf("PASS\n"); | |||||
| return 0; | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| Parsed input: { "foo\u0000bar": "qwerty\u0000asdf" } | |||||
| Result is `json_type_object` | |||||
| Key was present and same for `json_object_object_get_ex_len` and `json_object_object_get_len` | |||||
| Value is right type, `json_type_string` | |||||
| Expected value matches actual | |||||
| PASS | |||||
| @@ -0,0 +1 @@ | |||||
| test_basic.test | |||||