Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement json_compare() function. #684

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/apiref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1872,6 +1872,21 @@ only if they are exactly the same value, but also if they have equal
*NULL*.


Comparison
==========

.. function:: int json_compare(json_t *value1, json_t *value2)

Returns -1 if *value1* is less than *value2*, 0 if they are equal and 1 if
*value1* is greater than *value2*. This is akin to `strcmp()` and can be
used for comparing whole JSON data structures for sorting.

*NULL* is a valid parameter for either argument and will compare equal if
both are *NULL* or greater/less than if only one is NULL.

Nested arrays or objects will be recursively compared and any differences in
values, number of elements, object keys etc will compare consistently.

Copying
=======

Expand Down
1 change: 1 addition & 0 deletions src/jansson.def
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ EXPORTS
json_load_file
json_load_callback
json_equal
json_compare
json_copy
json_deep_copy
json_pack
Expand Down
4 changes: 4 additions & 0 deletions src/jansson.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,10 @@ json_t *json_vsprintf(const char *fmt, va_list ap)

int json_equal(const json_t *value1, const json_t *value2);

/* comparison */

int json_compare(const json_t *value1, const json_t *value2);

/* copying */

json_t *json_copy(json_t *value) JANSSON_ATTRS((warn_unused_result));
Expand Down
151 changes: 151 additions & 0 deletions src/value.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,66 @@ static int json_object_equal(const json_t *object1, const json_t *object2) {
return 1;
}

static int json_strnncmp(const char *s1, size_t s1_len, const char *s2, size_t s2_len)
{
int result = strncmp(s1, s2, (s1_len > s2_len ? s2_len : s1_len));

if (result > 1)
result = 1;
else if (result < -1)
result = -1;

if (result == 0) {
if (s1_len < s2_len)
result = -1;
else if (s1_len > s2_len)
result = 1;
}

return result;
}

static void json_object_next_key(const json_t *object, const char **next_key, size_t *next_key_len, const char *prev_key, size_t prev_key_len) {
const char *key;
size_t key_len;
const json_t *value;

*next_key = NULL;

/* Linear search through the object to find the smallest key which is >prev_key */
json_object_keylen_foreach((json_t *)object, key, key_len, value) {
if ((*next_key == NULL || json_strnncmp(*next_key, *next_key_len, key, key_len) < 0)
&& (prev_key == NULL || json_strnncmp(prev_key, prev_key_len, key, key_len) > 0)) {
*next_key = key;
*next_key_len = key_len;
}
}
}

static int json_object_compare(const json_t *object1, const json_t *object2) {
const char *key1 = NULL, *key2 = NULL;
size_t key1_len, key2_len;

json_object_next_key(object1, &key1, &key1_len, key1, key1_len);
json_object_next_key(object2, &key2, &key2_len, key2, key2_len);

while (key1 != NULL && key2 != NULL) {
int cmp = json_strnncmp(key1, key1_len, key2, key2_len);
if (cmp != 0)
return cmp;

json_object_next_key(object1, &key1, &key1_len, key1, key1_len);
json_object_next_key(object2, &key2, &key2_len, key2, key2_len);
}

if (key1 != NULL)
return 1;
else if(key2 != NULL)
return -1;
else
return 0;
}

static json_t *json_object_copy(json_t *object) {
json_t *result;

Expand Down Expand Up @@ -670,6 +730,33 @@ static int json_array_equal(const json_t *array1, const json_t *array2) {
return 1;
}

static int json_array_compare(const json_t *array1, const json_t *array2) {
size_t a1_size, a2_size, min_size, i;

a1_size = json_array_size(array1);
a2_size = json_array_size(array2);
min_size = a1_size > a2_size ? a2_size : a1_size;

for (i = 0; i < min_size; i++) {
json_t *value1, *value2;
int result;

value1 = json_array_get(array1, i);
value2 = json_array_get(array2, i);

result = json_compare(value1, value2);
if (result != 0)
return result;
}

if (a1_size < a2_size)
return 1;
else if (a1_size > a2_size)
return -1;

return 0;
}

static json_t *json_array_copy(json_t *array) {
json_t *result;
size_t i;
Expand Down Expand Up @@ -838,6 +925,15 @@ static int json_string_equal(const json_t *string1, const json_t *string2) {
return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length);
}

static int json_string_compare(const json_t *string1, const json_t *string2) {
json_string_t *s1, *s2;

s1 = json_to_string(string1);
s2 = json_to_string(string2);

return json_strnncmp(s1->value, s1->length, s2->value, s2->length);
}

static json_t *json_string_copy(const json_t *string) {
json_string_t *s;

Expand Down Expand Up @@ -922,6 +1018,15 @@ static int json_integer_equal(const json_t *integer1, const json_t *integer2) {
return json_integer_value(integer1) == json_integer_value(integer2);
}

static int json_integer_compare(const json_t *integer1, const json_t *integer2) {
if (json_integer_value(integer1) < json_integer_value(integer2))
return -1;
else if (json_integer_value(integer1) > json_integer_value(integer2))
return 1;
else
return 0;
}

static json_t *json_integer_copy(const json_t *integer) {
return json_integer(json_integer_value(integer));
}
Expand Down Expand Up @@ -965,6 +1070,15 @@ static int json_real_equal(const json_t *real1, const json_t *real2) {
return json_real_value(real1) == json_real_value(real2);
}

static int json_real_compare(const json_t *real1, const json_t *real2) {
if (json_real_value(real1) < json_real_value(real2))
return -1;
else if (json_real_value(real1) > json_real_value(real2))
return 1;
else
return 0;
}

static json_t *json_real_copy(const json_t *real) {
return json_real(json_real_value(real));
}
Expand Down Expand Up @@ -1055,6 +1169,43 @@ int json_equal(const json_t *json1, const json_t *json2) {
}
}

/*** comparison ***/

int json_compare(const json_t *json1, const json_t *json2) {
if (!json1 && !json2)
return 0;

if (!json1)
return -1;

if (!json2)
return 1;

if (json_typeof(json1) < json_typeof(json2))
return -1;
else if (json_typeof(json1) > json_typeof(json2))
return 1;

/* this covers true, false and null as they are singletons */
if (json1 == json2)
return 0;

switch (json_typeof(json1)) {
case JSON_OBJECT:
return json_object_compare(json1, json2);
case JSON_ARRAY:
return json_array_compare(json1, json2);
case JSON_STRING:
return json_string_compare(json1, json2);
case JSON_INTEGER:
return json_integer_compare(json1, json2);
case JSON_REAL:
return json_real_compare(json1, json2);
default:
return 0;
}
}

/*** copying ***/

json_t *json_copy(json_t *json) {
Expand Down
1 change: 1 addition & 0 deletions test/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ suites/api/test_cpp
suites/api/test_dump
suites/api/test_dump_callback
suites/api/test_equal
suites/api/test_compare
suites/api/test_fixed_size
suites/api/test_load
suites/api/test_load_callback
Expand Down
1 change: 1 addition & 0 deletions test/suites/api/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ check_PROGRAMS = \
test_dump \
test_dump_callback \
test_equal \
test_compare \
test_fixed_size \
test_load \
test_load_callback \
Expand Down
Loading