Skip to content

Commit b5c0dc3

Browse files
committed
Implement json_compare() function.
1 parent 53383b9 commit b5c0dc3

File tree

7 files changed

+357
-0
lines changed

7 files changed

+357
-0
lines changed

doc/apiref.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,6 +1872,21 @@ only if they are exactly the same value, but also if they have equal
18721872
*NULL*.
18731873

18741874

1875+
Comparison
1876+
==========
1877+
1878+
.. function:: int json_compare(json_t *value1, json_t *value2)
1879+
1880+
Returns -1 if *value1* is less than *value2*, 0 if they are equal and 1 if
1881+
*value1* is greater than *value2*. This is akin to `strcmp()` and can be
1882+
used for comparing whole JSON data structures for sorting.
1883+
1884+
*NULL* is a valid parameter for either argument and will compare equal if
1885+
both are *NULL* or greater/less than if only one is NULL.
1886+
1887+
Nested arrays or objects will be recursively compared and any differences in
1888+
values, number of elements, object keys etc will compare consistently.
1889+
18751890
Copying
18761891
=======
18771892

src/jansson.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ EXPORTS
6868
json_load_file
6969
json_load_callback
7070
json_equal
71+
json_compare
7172
json_copy
7273
json_deep_copy
7374
json_pack

src/jansson.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ json_t *json_vsprintf(const char *fmt, va_list ap)
348348

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

351+
/* comparison */
352+
353+
int json_compare(const json_t *value1, const json_t *value2);
354+
351355
/* copying */
352356

353357
json_t *json_copy(json_t *value) JANSSON_ATTRS((warn_unused_result));

src/value.c

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,66 @@ static int json_object_equal(const json_t *object1, const json_t *object2) {
370370
return 1;
371371
}
372372

373+
static int json_strnncmp(const char *s1, size_t s1_len, const char *s2, size_t s2_len)
374+
{
375+
int result = strncmp(s1, s2, (s1_len > s2_len ? s2_len : s1_len));
376+
377+
if (result > 1)
378+
result = 1;
379+
else if (result < -1)
380+
result = -1;
381+
382+
if (result == 0) {
383+
if (s1_len < s2_len)
384+
result = -1;
385+
else if (s1_len > s2_len)
386+
result = 1;
387+
}
388+
389+
return result;
390+
}
391+
392+
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) {
393+
const char *key;
394+
size_t key_len;
395+
const json_t *value;
396+
397+
*next_key = NULL;
398+
399+
/* Linear search through the object to find the smallest key which is >prev_key */
400+
json_object_keylen_foreach((json_t *)object, key, key_len, value) {
401+
if ((*next_key == NULL || json_strnncmp(*next_key, *next_key_len, key, key_len) < 0)
402+
&& (prev_key == NULL || json_strnncmp(prev_key, prev_key_len, key, key_len) > 0)) {
403+
*next_key = key;
404+
*next_key_len = key_len;
405+
}
406+
}
407+
}
408+
409+
static int json_object_compare(const json_t *object1, const json_t *object2) {
410+
const char *key1 = NULL, *key2 = NULL;
411+
size_t key1_len, key2_len;
412+
413+
json_object_next_key(object1, &key1, &key1_len, key1, key1_len);
414+
json_object_next_key(object2, &key2, &key2_len, key2, key2_len);
415+
416+
while (key1 != NULL && key2 != NULL) {
417+
int cmp = json_strnncmp(key1, key1_len, key2, key2_len);
418+
if (cmp != 0)
419+
return cmp;
420+
421+
json_object_next_key(object1, &key1, &key1_len, key1, key1_len);
422+
json_object_next_key(object2, &key2, &key2_len, key2, key2_len);
423+
}
424+
425+
if (key1 != NULL)
426+
return 1;
427+
else if(key2 != NULL)
428+
return -1;
429+
else
430+
return 0;
431+
}
432+
373433
static json_t *json_object_copy(json_t *object) {
374434
json_t *result;
375435

@@ -670,6 +730,33 @@ static int json_array_equal(const json_t *array1, const json_t *array2) {
670730
return 1;
671731
}
672732

733+
static int json_array_compare(const json_t *array1, const json_t *array2) {
734+
size_t a1_size, a2_size, min_size, i;
735+
736+
a1_size = json_array_size(array1);
737+
a2_size = json_array_size(array2);
738+
min_size = a1_size > a2_size ? a2_size : a1_size;
739+
740+
for (i = 0; i < min_size; i++) {
741+
json_t *value1, *value2;
742+
int result;
743+
744+
value1 = json_array_get(array1, i);
745+
value2 = json_array_get(array2, i);
746+
747+
result = json_compare(value1, value2);
748+
if (result != 0)
749+
return result;
750+
}
751+
752+
if (a1_size < a2_size)
753+
return 1;
754+
else if (a1_size > a2_size)
755+
return -1;
756+
757+
return 0;
758+
}
759+
673760
static json_t *json_array_copy(json_t *array) {
674761
json_t *result;
675762
size_t i;
@@ -838,6 +925,15 @@ static int json_string_equal(const json_t *string1, const json_t *string2) {
838925
return s1->length == s2->length && !memcmp(s1->value, s2->value, s1->length);
839926
}
840927

928+
static int json_string_compare(const json_t *string1, const json_t *string2) {
929+
json_string_t *s1, *s2;
930+
931+
s1 = json_to_string(string1);
932+
s2 = json_to_string(string2);
933+
934+
return json_strnncmp(s1->value, s1->length, s2->value, s2->length);
935+
}
936+
841937
static json_t *json_string_copy(const json_t *string) {
842938
json_string_t *s;
843939

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

1021+
static int json_integer_compare(const json_t *integer1, const json_t *integer2) {
1022+
if (json_integer_value(integer1) < json_integer_value(integer2))
1023+
return -1;
1024+
else if (json_integer_value(integer1) > json_integer_value(integer2))
1025+
return 1;
1026+
else
1027+
return 0;
1028+
}
1029+
9251030
static json_t *json_integer_copy(const json_t *integer) {
9261031
return json_integer(json_integer_value(integer));
9271032
}
@@ -965,6 +1070,15 @@ static int json_real_equal(const json_t *real1, const json_t *real2) {
9651070
return json_real_value(real1) == json_real_value(real2);
9661071
}
9671072

1073+
static int json_real_compare(const json_t *real1, const json_t *real2) {
1074+
if (json_real_value(real1) < json_real_value(real2))
1075+
return -1;
1076+
else if (json_real_value(real1) > json_real_value(real2))
1077+
return 1;
1078+
else
1079+
return 0;
1080+
}
1081+
9681082
static json_t *json_real_copy(const json_t *real) {
9691083
return json_real(json_real_value(real));
9701084
}
@@ -1055,6 +1169,43 @@ int json_equal(const json_t *json1, const json_t *json2) {
10551169
}
10561170
}
10571171

1172+
/*** comparison ***/
1173+
1174+
int json_compare(const json_t *json1, const json_t *json2) {
1175+
if (!json1 && !json2)
1176+
return 0;
1177+
1178+
if (!json1)
1179+
return -1;
1180+
1181+
if (!json2)
1182+
return 1;
1183+
1184+
if (json_typeof(json1) < json_typeof(json2))
1185+
return -1;
1186+
else if (json_typeof(json1) > json_typeof(json2))
1187+
return 1;
1188+
1189+
/* this covers true, false and null as they are singletons */
1190+
if (json1 == json2)
1191+
return 0;
1192+
1193+
switch (json_typeof(json1)) {
1194+
case JSON_OBJECT:
1195+
return json_object_compare(json1, json2);
1196+
case JSON_ARRAY:
1197+
return json_array_compare(json1, json2);
1198+
case JSON_STRING:
1199+
return json_string_compare(json1, json2);
1200+
case JSON_INTEGER:
1201+
return json_integer_compare(json1, json2);
1202+
case JSON_REAL:
1203+
return json_real_compare(json1, json2);
1204+
default:
1205+
return 0;
1206+
}
1207+
}
1208+
10581209
/*** copying ***/
10591210

10601211
json_t *json_copy(json_t *json) {

test/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ suites/api/test_cpp
77
suites/api/test_dump
88
suites/api/test_dump_callback
99
suites/api/test_equal
10+
suites/api/test_compare
1011
suites/api/test_fixed_size
1112
suites/api/test_load
1213
suites/api/test_load_callback

test/suites/api/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ check_PROGRAMS = \
77
test_dump \
88
test_dump_callback \
99
test_equal \
10+
test_compare \
1011
test_fixed_size \
1112
test_load \
1213
test_load_callback \

0 commit comments

Comments
 (0)