diff --git a/doc/apiref.rst b/doc/apiref.rst index a2a0794..0a96999 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -493,6 +493,16 @@ The following functions implement an iteration protocol for objects: Returns an opaque iterator which can be used to iterate over all key-value pairs in *object*, or *NULL* if *object* is empty. +.. cfunction:: void *json_object_iter_at(json_t *object, const char *key) + + Like :cfunc:`json_object_iter()`, but returns an iterator to the + key-value pair in *object* whose key is equal to *key*, or NULL if + *key* is not found in *object*. Iterating forward to the end of + *object* only yields all key-value pairs of the object if *key* + happens to be the first key in the underlying hash table. + + .. versionadded:: 1.3 + .. cfunction:: void *json_object_iter_next(json_t *object, void *iter) Returns an iterator pointing to the next key-value pair in *object* @@ -509,6 +519,21 @@ The following functions implement an iteration protocol for objects: Extract the associated value from *iter*. +.. cfunction:: int json_object_iter_set(json_t *object, void *iter, json_t *value) + + Set the value of the key-value pair in *object*, that is pointed to + by *iter*, to *value*. + + .. versionadded:: 1.3 + +.. cfunction:: int json_object_iter_set_new(json_t *object, void *iter, json_t *value) + + Like :cfunc:`json_object_iter_set()`, but steals the reference to + *value*. This is useful when *value* is newly created and not used + after the call. + + .. versionadded:: 1.3 + The iteration protocol can be used for example as follows:: /* obj is a JSON object */ diff --git a/src/hashtable.c b/src/hashtable.c index 05dc167..990fd6e 100644 --- a/src/hashtable.c +++ b/src/hashtable.c @@ -318,6 +318,22 @@ void *hashtable_iter(hashtable_t *hashtable) return hashtable_iter_next(hashtable, &hashtable->list); } +void *hashtable_iter_at(hashtable_t *hashtable, const void *key) +{ + pair_t *pair; + unsigned int hash; + bucket_t *bucket; + + hash = hashtable->hash_key(key); + bucket = &hashtable->buckets[hash % num_buckets(hashtable)]; + + pair = hashtable_find_pair(hashtable, bucket, key, hash); + if(!pair) + return NULL; + + return &pair->list; +} + void *hashtable_iter_next(hashtable_t *hashtable, void *iter) { list_t *list = (list_t *)iter; @@ -337,3 +353,13 @@ void *hashtable_iter_value(void *iter) pair_t *pair = list_to_pair((list_t *)iter); return pair->value; } + +void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value) +{ + pair_t *pair = list_to_pair((list_t *)iter); + + if(hashtable->free_value) + hashtable->free_value(pair->value); + + pair->value = value; +} diff --git a/src/hashtable.h b/src/hashtable.h index 81a0af5..e920c6b 100644 --- a/src/hashtable.h +++ b/src/hashtable.h @@ -160,6 +160,17 @@ void hashtable_clear(hashtable_t *hashtable); */ void *hashtable_iter(hashtable_t *hashtable); +/** + * hashtable_iter - Return an iterator at a specific key + * + * @hashtable: The hashtable object + * @key: The key that the iterator should point to + * + * Like hashtable_iter() but returns an iterator pointing to a + * specific key. + */ +void *hashtable_iter_at(hashtable_t *hashtable, const void *key); + /** * hashtable_iter_next - Advance an iterator * @@ -185,4 +196,12 @@ void *hashtable_iter_key(void *iter); */ void *hashtable_iter_value(void *iter); +/** + * hashtable_iter_set - Set the value pointed by an iterator + * + * @iter: The iterator + * @value: The value to set + */ +void hashtable_iter_set(hashtable_t *hashtable, void *iter, void *value); + #endif diff --git a/src/jansson.h b/src/jansson.h index 73f6ce0..d703c7b 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -83,9 +83,11 @@ int json_object_del(json_t *object, const char *key); int json_object_clear(json_t *object); int json_object_update(json_t *object, json_t *other); void *json_object_iter(json_t *object); +void *json_object_iter_at(json_t *object, const char *key); void *json_object_iter_next(json_t *object, void *iter); const char *json_object_iter_key(void *iter); json_t *json_object_iter_value(void *iter); +int json_object_iter_set_new(json_t *object, void *iter, json_t *value); static inline int json_object_set(json_t *object, const char *key, json_t *value) @@ -99,6 +101,12 @@ int json_object_set_nocheck(json_t *object, const char *key, json_t *value) return json_object_set_new_nocheck(object, key, json_incref(value)); } +static inline +int json_object_iter_set(json_t *object, void *iter, json_t *value) +{ + return json_object_iter_set_new(object, iter, json_incref(value)); +} + unsigned int json_array_size(const json_t *array); json_t *json_array_get(const json_t *array, unsigned int index); int json_array_set_new(json_t *array, unsigned int index, json_t *value); diff --git a/src/value.c b/src/value.c index 01e180e..31109ff 100644 --- a/src/value.c +++ b/src/value.c @@ -190,6 +190,17 @@ void *json_object_iter(json_t *json) return hashtable_iter(&object->hashtable); } +void *json_object_iter_at(json_t *json, const char *key) +{ + json_object_t *object; + + if(!key || !json_is_object(json)) + return NULL; + + object = json_to_object(json); + return hashtable_iter_at(&object->hashtable, key); +} + void *json_object_iter_next(json_t *json, void *iter) { json_object_t *object; @@ -217,6 +228,19 @@ json_t *json_object_iter_value(void *iter) return (json_t *)hashtable_iter_value(iter); } +int json_object_iter_set_new(json_t *json, void *iter, json_t *value) +{ + json_object_t *object; + + if(!json_is_object(json) || !iter || !value) + return -1; + + object = json_to_object(json); + hashtable_iter_set(&object->hashtable, iter, value); + + return 0; +} + static int json_object_equal(json_t *object1, json_t *object2) { void *iter; diff --git a/test/suites/api/check-exports b/test/suites/api/check-exports index 178abeb..a6d877f 100755 --- a/test/suites/api/check-exports +++ b/test/suites/api/check-exports @@ -39,9 +39,11 @@ json_object_del json_object_clear json_object_update json_object_iter +json_object_iter_at json_object_iter_next json_object_iter_key json_object_iter_value +json_object_iter_set_new json_dumps json_dumpf json_dump_file diff --git a/test/suites/api/test_object.c b/test/suites/api/test_object.c index 849dac0..99d0a56 100644 --- a/test/suites/api/test_object.c +++ b/test/suites/api/test_object.c @@ -258,6 +258,36 @@ static void test_iterators() if(json_object_iter_next(object, iter) != NULL) fail("able to iterate over the end"); + if(json_object_iter_at(object, "foo")) + fail("json_object_iter_at() succeeds for non-existent key"); + + iter = json_object_iter_at(object, "b"); + if(!iter) + fail("json_object_iter_at() fails for an existing key"); + + if(strcmp(json_object_iter_key(iter), "b")) + fail("iterating failed: wrong key"); + if(json_object_iter_value(iter) != bar) + fail("iterating failed: wrong value"); + + iter = json_object_iter_next(object, iter); + if(!iter) + fail("unable to increment iterator"); + if(strcmp(json_object_iter_key(iter), "c")) + fail("iterating failed: wrong key"); + if(json_object_iter_value(iter) != baz) + fail("iterating failed: wrong value"); + + if(json_object_iter_set(object, iter, bar)) + fail("unable to set value at iterator"); + + if(strcmp(json_object_iter_key(iter), "c")) + fail("json_object_iter_key() fails after json_object_iter_set()"); + if(json_object_iter_value(iter) != bar) + fail("json_object_iter_value() fails after json_object_iter_set()"); + if(json_object_get(object, "c") != bar) + fail("json_object_get() fails after json_object_iter_set()"); + json_decref(object); json_decref(foo); json_decref(bar);