Merge pull request #505 from AllenX2018/recursive_object_update
Recursive object update
This commit is contained in:
commit
8b022dad76
@ -708,6 +708,12 @@ allowed in object keys.
|
|||||||
|
|
||||||
.. versionadded:: 2.3
|
.. versionadded:: 2.3
|
||||||
|
|
||||||
|
.. function:: int json_object_update_recursive(json_t *object, json_t *other)
|
||||||
|
|
||||||
|
Like :func:`json_object_update()`, but object values in *other* are
|
||||||
|
recursively merged with the corresponding values in *object* if they are also
|
||||||
|
objects, instead of overwriting them. Returns 0 on success or -1 on error.
|
||||||
|
|
||||||
.. function:: json_object_foreach(object, key, value)
|
.. function:: json_object_foreach(object, key, value)
|
||||||
|
|
||||||
Iterate over every key-value pair of ``object``, running the block
|
Iterate over every key-value pair of ``object``, running the block
|
||||||
|
@ -41,6 +41,7 @@ EXPORTS
|
|||||||
json_object_update
|
json_object_update
|
||||||
json_object_update_existing
|
json_object_update_existing
|
||||||
json_object_update_missing
|
json_object_update_missing
|
||||||
|
json_object_update_recursive
|
||||||
json_object_iter
|
json_object_iter
|
||||||
json_object_iter_at
|
json_object_iter_at
|
||||||
json_object_iter_next
|
json_object_iter_next
|
||||||
|
@ -199,6 +199,7 @@ int json_object_clear(json_t *object);
|
|||||||
int json_object_update(json_t *object, json_t *other);
|
int json_object_update(json_t *object, json_t *other);
|
||||||
int json_object_update_existing(json_t *object, json_t *other);
|
int json_object_update_existing(json_t *object, json_t *other);
|
||||||
int json_object_update_missing(json_t *object, json_t *other);
|
int json_object_update_missing(json_t *object, json_t *other);
|
||||||
|
int json_object_update_recursive(json_t *object, json_t *other);
|
||||||
void *json_object_iter(json_t *object);
|
void *json_object_iter(json_t *object);
|
||||||
void *json_object_iter_at(json_t *object, const char *key);
|
void *json_object_iter_at(json_t *object, const char *key);
|
||||||
void *json_object_key_to_iter(const char *key);
|
void *json_object_key_to_iter(const char *key);
|
||||||
|
62
src/value.c
62
src/value.c
@ -37,7 +37,7 @@ static JSON_INLINE int isnan(double x) { return x != x; }
|
|||||||
static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
|
static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
json_t *do_deep_copy(const json_t *, hashtable_t *);
|
json_t *do_deep_copy(const json_t *json, hashtable_t *parents);
|
||||||
|
|
||||||
static JSON_INLINE void json_init(json_t *json, json_type type)
|
static JSON_INLINE void json_init(json_t *json, json_type type)
|
||||||
{
|
{
|
||||||
@ -214,6 +214,58 @@ int json_object_update_missing(json_t *object, json_t *other)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int do_object_update_recursive(json_t *object, json_t *other, hashtable_t *parents)
|
||||||
|
{
|
||||||
|
const char *key;
|
||||||
|
json_t *value;
|
||||||
|
char loop_key[LOOP_KEY_LEN];
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
|
if(!json_is_object(object) || !json_is_object(other))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if(jsonp_loop_check(parents, other, loop_key, sizeof(loop_key)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
json_object_foreach(other, key, value) {
|
||||||
|
json_t *v = json_object_get(object, key);
|
||||||
|
|
||||||
|
if(json_is_object(v) && json_is_object(value))
|
||||||
|
{
|
||||||
|
if(do_object_update_recursive(v, value, parents))
|
||||||
|
{
|
||||||
|
res = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(json_object_set_nocheck(object, key, value))
|
||||||
|
{
|
||||||
|
res = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashtable_del(parents, loop_key);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int json_object_update_recursive(json_t *object, json_t *other)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
hashtable_t parents_set;
|
||||||
|
|
||||||
|
if (hashtable_init(&parents_set))
|
||||||
|
return -1;
|
||||||
|
res = do_object_update_recursive(object, other, &parents_set);
|
||||||
|
hashtable_close(&parents_set);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void *json_object_iter(json_t *json)
|
void *json_object_iter(json_t *json)
|
||||||
{
|
{
|
||||||
json_object_t *object;
|
json_object_t *object;
|
||||||
@ -329,7 +381,7 @@ static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents)
|
|||||||
|
|
||||||
result = json_object();
|
result = json_object();
|
||||||
if(!result)
|
if(!result)
|
||||||
return NULL;
|
goto out;
|
||||||
|
|
||||||
/* Cannot use json_object_foreach because object has to be cast
|
/* Cannot use json_object_foreach because object has to be cast
|
||||||
non-const */
|
non-const */
|
||||||
@ -348,6 +400,8 @@ static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents)
|
|||||||
}
|
}
|
||||||
iter = json_object_iter_next((json_t *)object, iter);
|
iter = json_object_iter_next((json_t *)object, iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
hashtable_del(parents, loop_key);
|
hashtable_del(parents, loop_key);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -648,7 +702,7 @@ static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents)
|
|||||||
|
|
||||||
result = json_array();
|
result = json_array();
|
||||||
if(!result)
|
if(!result)
|
||||||
return NULL;
|
goto out;
|
||||||
|
|
||||||
for(i = 0; i < json_array_size(array); i++)
|
for(i = 0; i < json_array_size(array); i++)
|
||||||
{
|
{
|
||||||
@ -659,6 +713,8 @@ static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
hashtable_del(parents, loop_key);
|
hashtable_del(parents, loop_key);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -207,6 +207,86 @@ static void test_conditional_updates()
|
|||||||
json_decref(other);
|
json_decref(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_recursive_updates()
|
||||||
|
{
|
||||||
|
json_t *invalid, *object, *other, *barBefore, *barAfter;
|
||||||
|
|
||||||
|
invalid = json_integer(42);
|
||||||
|
|
||||||
|
object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2);
|
||||||
|
other = json_pack("{sisisi}", "foo", 3, "bar", 4, "baz", 5);
|
||||||
|
|
||||||
|
if(!json_object_update_recursive(invalid, other))
|
||||||
|
fail("json_object_update_recursive accepted non-object argument");
|
||||||
|
|
||||||
|
json_decref(invalid);
|
||||||
|
|
||||||
|
if(json_object_update_recursive(object, other))
|
||||||
|
fail("json_object_update_recursive failed");
|
||||||
|
|
||||||
|
if(json_object_size(object) != 3)
|
||||||
|
fail("invalid size after update");
|
||||||
|
|
||||||
|
if(json_integer_value(json_object_get(object, "foo")) != 3)
|
||||||
|
fail("json_object_update_recursive failed to update existing key");
|
||||||
|
|
||||||
|
if(json_integer_value(json_object_get(object, "bar")) != 4)
|
||||||
|
fail("json_object_update_recursive failed to overwrite object");
|
||||||
|
|
||||||
|
if(json_integer_value(json_object_get(object, "baz")) != 5)
|
||||||
|
fail("json_object_update_recursive didn't add new item");
|
||||||
|
|
||||||
|
json_decref(object);
|
||||||
|
json_decref(other);
|
||||||
|
|
||||||
|
object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2);
|
||||||
|
other = json_pack("{s{si}}", "bar", "baz", 3);
|
||||||
|
barBefore = json_object_get(object, "bar");
|
||||||
|
|
||||||
|
if(!barBefore)
|
||||||
|
fail("can't get bar object before json_object_update_recursive");
|
||||||
|
|
||||||
|
if(json_object_update_recursive(object, other))
|
||||||
|
fail("json_object_update_recursive failed");
|
||||||
|
|
||||||
|
if(json_object_size(object) != 2)
|
||||||
|
fail("invalid size after update");
|
||||||
|
|
||||||
|
if(!json_object_get(object, "foo"))
|
||||||
|
fail("json_object_update_recursive removed existing key");
|
||||||
|
|
||||||
|
if(json_integer_value(json_object_get(json_object_get(object, "bar"), "baz")) != 3)
|
||||||
|
fail("json_object_update_recursive failed to update nested value");
|
||||||
|
|
||||||
|
barAfter = json_object_get(object, "bar");
|
||||||
|
if(!barAfter)
|
||||||
|
fail("can't get bar object after json_object_update_recursive");
|
||||||
|
|
||||||
|
if(barBefore != barAfter)
|
||||||
|
fail("bar object reference changed after json_object_update_recursive");
|
||||||
|
|
||||||
|
json_decref(object);
|
||||||
|
json_decref(other);
|
||||||
|
|
||||||
|
/* check circular reference */
|
||||||
|
object = json_pack("{s{s{s{si}}}}", "foo", "bar", "baz", "xxx", 2);
|
||||||
|
other = json_pack("{s{s{si}}}", "foo", "bar", "baz", 2);
|
||||||
|
json_object_set(json_object_get(json_object_get(other, "foo"), "bar"), "baz",
|
||||||
|
json_object_get(other, "foo"));
|
||||||
|
|
||||||
|
if(!json_object_update_recursive(object, other))
|
||||||
|
fail("json_object_update_recursive update a circular reference!");
|
||||||
|
|
||||||
|
json_object_set_new(json_object_get(json_object_get(other, "foo"), "bar"), "baz",
|
||||||
|
json_integer(1));
|
||||||
|
|
||||||
|
if(json_object_update_recursive(object, other))
|
||||||
|
fail("json_object_update_recursive failed!");
|
||||||
|
|
||||||
|
json_decref(object);
|
||||||
|
json_decref(other);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_circular()
|
static void test_circular()
|
||||||
{
|
{
|
||||||
json_t *object1, *object2;
|
json_t *object1, *object2;
|
||||||
@ -667,6 +747,7 @@ static void run_tests()
|
|||||||
test_update();
|
test_update();
|
||||||
test_set_many_keys();
|
test_set_many_keys();
|
||||||
test_conditional_updates();
|
test_conditional_updates();
|
||||||
|
test_recursive_updates();
|
||||||
test_circular();
|
test_circular();
|
||||||
test_set_nocheck();
|
test_set_nocheck();
|
||||||
test_iterators();
|
test_iterators();
|
||||||
|
Loading…
Reference in New Issue
Block a user