| @@ -165,6 +165,9 @@ JSONC_0.15 { | |||
| } JSONC_0.14; | |||
| 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; | |||
| @@ -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, | |||
| 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 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) | |||
| { | |||
| 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) | |||
| @@ -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 *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; | |||
| } | |||
| json_bool json_object_object_get_ex(const struct json_object *jso, const char *key, | |||
| 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) | |||
| *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, | |||
| 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 | |||
| * | |||
| * 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 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(struct json_object *obj, const char *const key, | |||
| 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. | |||
| * 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 | |||
| * 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(). | |||
| @@ -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). | |||
| * | |||
| * @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 | |||
| */ | |||
| 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. | |||
| * | |||
| @@ -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. | |||
| * | |||
| * @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 | |||
| * associated with the given field name. | |||
| * | |||
| * \n | |||
| * It is safe to pass a NULL value. | |||
| * @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 | |||
| * | |||
| @@ -26,6 +26,8 @@ set(ALL_TEST_NAMES | |||
| test_int_add | |||
| test_locale | |||
| test_null | |||
| test_null_keys_add | |||
| test_null_keys_get | |||
| test_parse | |||
| test_parse_int64 | |||
| 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 | |||