Skip to content

Commit

Permalink
Implement json_compare() function.
Browse files Browse the repository at this point in the history
  • Loading branch information
solemnwarning committed Mar 25, 2024
1 parent 53383b9 commit b5c0dc3
Show file tree
Hide file tree
Showing 7 changed files with 357 additions and 0 deletions.
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

0 comments on commit b5c0dc3

Please sign in to comment.