From a3079747313f8d4f2e3954b29a1fb7ee67cd5d2e Mon Sep 17 00:00:00 2001 From: Petri Lehtinen Date: Tue, 24 Jan 2012 20:37:08 +0200 Subject: [PATCH] Implement json_object_foreach() Also change many places to use it internally to replace hand-crafted iteration. Closes #45, #46. --- doc/apiref.rst | 40 +++++++++++++++++++++ src/hashtable.h | 4 +++ src/jansson.h | 6 ++++ src/value.c | 68 ++++++++++++----------------------- test/suites/api/check-exports | 1 + test/suites/api/test_object.c | 19 ++++++++++ 6 files changed, 92 insertions(+), 46 deletions(-) diff --git a/doc/apiref.rst b/doc/apiref.rst index 5003a04..3820b3f 100644 --- a/doc/apiref.rst +++ b/doc/apiref.rst @@ -565,6 +565,36 @@ Unicode string and the value is any JSON value. existing keys. Returns 0 on success or -1 on error. +The following macro can be used to iterate through all key-value pairs +in an object. + +.. function:: json_object_foreach(object, key, value) + + Iterate over every key-value pair of ``object``, running the block + of code that follows each time with the proper values set to + variables ``key`` and ``value``, of types :type:`const char *` and + :type:`json_t *` respectively. Example:: + + /* obj is a JSON object */ + const char *key; + json_t *value; + + json_object_foreach(obj, key, value) { + /* block of code that uses key and value */ + } + + The items are not returned in any particular order. + + This macro expands to an ordinary ``for`` statement upon + preprocessing, so its performance is equivalent to that of + hand-written iteration code using the object iteration protocol + (see below). The main advantage of this macro is that it abstracts + away the complexity behind iteration, and makes for shorter, more + concise code. + + .. versionadded:: 2.3 + + The following functions implement an iteration protocol for objects, allowing to iterate through all key-value pairs in an object. The items are not returned in any particular order, as this would require @@ -610,11 +640,21 @@ sorting due to the internal hashtable implementation. *value*. This is useful when *value* is newly created and not used after the call. +.. function:: void *json_object_key_to_iter(const char *key) + + Like :func:`json_object_iter_at()`, but much faster. Only works for + values returned by :func:`json_object_iter_key()`. Using other keys + will lead to segfaults. This function is used internally to + implement :func:`json_object_foreach`. + + .. versionadded:: 2.3 + The iteration protocol can be used for example as follows:: /* obj is a JSON object */ const char *key; json_t *value; + void *iter = json_object_iter(obj); while(iter) { diff --git a/src/hashtable.h b/src/hashtable.h index e8d5ccb..beee8ea 100644 --- a/src/hashtable.h +++ b/src/hashtable.h @@ -40,6 +40,10 @@ typedef struct hashtable { struct hashtable_list list; } hashtable_t; + +#define hashtable_key_to_iter(key_) \ + (&(container_of(key_, struct hashtable_pair, key)->list)) + /** * hashtable_init - Initialize a hashtable object * diff --git a/src/jansson.h b/src/jansson.h index ca9ac11..274dbad 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -132,11 +132,17 @@ 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_key_to_iter(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); +#define json_object_foreach(object, key, value) \ + for(key = json_object_iter_key(json_object_iter(object)); \ + key && (value = json_object_iter_value(json_object_key_to_iter(key))); \ + key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key)))) + static JSON_INLINE int json_object_set(json_t *object, const char *key, json_t *value) { diff --git a/src/value.c b/src/value.c index c728427..eadf596 100644 --- a/src/value.c +++ b/src/value.c @@ -135,23 +135,15 @@ int json_object_clear(json_t *json) int json_object_update(json_t *object, json_t *other) { - void *iter; + const char *key; + json_t *value; if(!json_is_object(object) || !json_is_object(other)) return -1; - iter = json_object_iter(other); - while(iter) { - const char *key; - json_t *value; - - key = json_object_iter_key(iter); - value = json_object_iter_value(iter); - + json_object_foreach(other, key, value) { if(json_object_set_nocheck(object, key, value)) return -1; - - iter = json_object_iter_next(other, iter); } return 0; @@ -215,27 +207,27 @@ int json_object_iter_set_new(json_t *json, void *iter, json_t *value) return 0; } +void *json_object_key_to_iter(const char *key) +{ + if(!key) + return NULL; + + return hashtable_key_to_iter(key); +} + static int json_object_equal(json_t *object1, json_t *object2) { - void *iter; + const char *key; + json_t *value1, *value2; if(json_object_size(object1) != json_object_size(object2)) return 0; - iter = json_object_iter(object1); - while(iter) - { - const char *key; - json_t *value1, *value2; - - key = json_object_iter_key(iter); - value1 = json_object_iter_value(iter); + json_object_foreach(object1, key, value1) { value2 = json_object_get(object2, key); if(!json_equal(value1, value2)) return 0; - - iter = json_object_iter_next(object1, iter); } return 1; @@ -244,50 +236,34 @@ static int json_object_equal(json_t *object1, json_t *object2) static json_t *json_object_copy(json_t *object) { json_t *result; - void *iter; + + const char *key; + json_t *value; result = json_object(); if(!result) return NULL; - iter = json_object_iter(object); - while(iter) - { - const char *key; - json_t *value; - - key = json_object_iter_key(iter); - value = json_object_iter_value(iter); + json_object_foreach(object, key, value) json_object_set_nocheck(result, key, value); - iter = json_object_iter_next(object, iter); - } - return result; } static json_t *json_object_deep_copy(json_t *object) { json_t *result; - void *iter; + + const char *key; + json_t *value; result = json_object(); if(!result) return NULL; - iter = json_object_iter(object); - while(iter) - { - const char *key; - json_t *value; - - key = json_object_iter_key(iter); - value = json_object_iter_value(iter); + json_object_foreach(object, key, value) json_object_set_new_nocheck(result, key, json_deep_copy(value)); - iter = json_object_iter_next(object, iter); - } - return result; } diff --git a/test/suites/api/check-exports b/test/suites/api/check-exports index 802d683..0af0dd0 100755 --- a/test/suites/api/check-exports +++ b/test/suites/api/check-exports @@ -44,6 +44,7 @@ json_object_iter_next json_object_iter_key json_object_iter_value json_object_iter_set_new +json_object_key_to_iter json_dumps json_dumpf json_dump_file diff --git a/test/suites/api/test_object.c b/test/suites/api/test_object.c index 59f85b9..4a19016 100644 --- a/test/suites/api/test_object.c +++ b/test/suites/api/test_object.c @@ -437,6 +437,24 @@ static void test_preserve_order() json_decref(object); } +static void test_foreach() +{ + const char *key; + json_t *object1, *object2, *value; + + object1 = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3); + object2 = json_object(); + + json_object_foreach(object1, key, value) + json_object_set(object2, key, value); + + if(!json_equal(object1, object2)) + fail("json_object_foreach failed to iterate all key-value pairs"); + + json_decref(object1); + json_decref(object2); +} + static void run_tests() { test_misc(); @@ -446,4 +464,5 @@ static void run_tests() test_set_nocheck(); test_iterators(); test_preserve_order(); + test_foreach(); }