Add equality test for JSON values
This commit is contained in:
parent
22173c1d8b
commit
95a468cebb
@ -673,3 +673,43 @@ The following functions perform the actual JSON decoding.
|
|||||||
object it contains, or *NULL* on error, in which case *error* is
|
object it contains, or *NULL* on error, in which case *error* is
|
||||||
filled with information about the error. See above for discussion
|
filled with information about the error. See above for discussion
|
||||||
on the *error* parameter.
|
on the *error* parameter.
|
||||||
|
|
||||||
|
|
||||||
|
Equality
|
||||||
|
========
|
||||||
|
|
||||||
|
Testing for equality of two JSON values cannot, in general, be
|
||||||
|
achieved using the ``==`` operator. Equality in the terms of the
|
||||||
|
``==`` operator states that the two :ctype:`json_t` pointers point to
|
||||||
|
exactly the same JSON value. However, two JSON values can be equal not
|
||||||
|
only if they are exactly the same value, but also if they have equal
|
||||||
|
"contents":
|
||||||
|
|
||||||
|
* Two integer or real values are equal if their contained numeric
|
||||||
|
values are equal. An integer value is never equal to a real value,
|
||||||
|
though.
|
||||||
|
|
||||||
|
* Two strings are equal if their contained UTF-8 strings are equal.
|
||||||
|
|
||||||
|
* Two arrays are equal if they have the same number of elements and
|
||||||
|
each element in the first array is equal to the corresponding
|
||||||
|
element in the second array.
|
||||||
|
|
||||||
|
* Two objects are equal if they have exactly the same keys and the
|
||||||
|
value for each key in the first object is equal to the value of the
|
||||||
|
corresponding key in the second object.
|
||||||
|
|
||||||
|
* Two true, false or null values have no "contents", so they are equal
|
||||||
|
if their types are equal. (Because these values are singletons,
|
||||||
|
their equality can actually be tested with ``==``.)
|
||||||
|
|
||||||
|
The following function can be used to test whether two JSON values are
|
||||||
|
equal.
|
||||||
|
|
||||||
|
.. cfunction:: int json_equal(json_t *value1, json_t *value2)
|
||||||
|
|
||||||
|
Returns 1 if *value1* and *value2* are equal, as defined above.
|
||||||
|
Returns 0 if they are inequal or one or both of the pointers are
|
||||||
|
*NULL*.
|
||||||
|
|
||||||
|
.. versionadded:: 1.2
|
||||||
|
@ -137,6 +137,11 @@ int json_integer_set(json_t *integer, int value);
|
|||||||
int json_real_set(json_t *real, double value);
|
int json_real_set(json_t *real, double value);
|
||||||
|
|
||||||
|
|
||||||
|
/* equality */
|
||||||
|
|
||||||
|
int json_equal(json_t *value1, json_t *value2);
|
||||||
|
|
||||||
|
|
||||||
/* loading, printing */
|
/* loading, printing */
|
||||||
|
|
||||||
#define JSON_ERROR_TEXT_LENGTH 160
|
#define JSON_ERROR_TEXT_LENGTH 160
|
||||||
|
93
src/value.c
93
src/value.c
@ -217,6 +217,32 @@ json_t *json_object_iter_value(void *iter)
|
|||||||
return (json_t *)hashtable_iter_value(iter);
|
return (json_t *)hashtable_iter_value(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int json_object_equal(json_t *object1, json_t *object2)
|
||||||
|
{
|
||||||
|
void *iter;
|
||||||
|
|
||||||
|
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);
|
||||||
|
value2 = json_object_get(object2, key);
|
||||||
|
|
||||||
|
if(!json_equal(value1, value2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
iter = json_object_iter_next(object1, iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*** array ***/
|
/*** array ***/
|
||||||
|
|
||||||
@ -463,6 +489,28 @@ int json_array_extend(json_t *json, json_t *other_json)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int json_array_equal(json_t *array1, json_t *array2)
|
||||||
|
{
|
||||||
|
unsigned int i, size;
|
||||||
|
|
||||||
|
size = json_array_size(array1);
|
||||||
|
if(size != json_array_size(array2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for(i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
json_t *value1, *value2;
|
||||||
|
|
||||||
|
value1 = json_array_get(array1, i);
|
||||||
|
value2 = json_array_get(array2, i);
|
||||||
|
|
||||||
|
if(!json_equal(value1, value2))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*** string ***/
|
/*** string ***/
|
||||||
|
|
||||||
@ -533,6 +581,10 @@ static void json_delete_string(json_string_t *string)
|
|||||||
free(string);
|
free(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int json_string_equal(json_t *string1, json_t *string2)
|
||||||
|
{
|
||||||
|
return strcmp(json_string_value(string1), json_string_value(string2)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*** integer ***/
|
/*** integer ***/
|
||||||
|
|
||||||
@ -570,6 +622,10 @@ static void json_delete_integer(json_integer_t *integer)
|
|||||||
free(integer);
|
free(integer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int json_integer_equal(json_t *integer1, json_t *integer2)
|
||||||
|
{
|
||||||
|
return json_integer_value(integer1) == json_integer_value(integer2);
|
||||||
|
}
|
||||||
|
|
||||||
/*** real ***/
|
/*** real ***/
|
||||||
|
|
||||||
@ -607,6 +663,10 @@ static void json_delete_real(json_real_t *real)
|
|||||||
free(real);
|
free(real);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int json_real_equal(json_t *real1, json_t *real2)
|
||||||
|
{
|
||||||
|
return json_real_value(real1) == json_real_value(real2);
|
||||||
|
}
|
||||||
|
|
||||||
/*** number ***/
|
/*** number ***/
|
||||||
|
|
||||||
@ -674,3 +734,36 @@ void json_delete(json_t *json)
|
|||||||
|
|
||||||
/* json_delete is not called for true, false or null */
|
/* json_delete is not called for true, false or null */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*** equality ***/
|
||||||
|
|
||||||
|
int json_equal(json_t *json1, json_t *json2)
|
||||||
|
{
|
||||||
|
if(!json1 || !json2)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if(json_typeof(json1) != json_typeof(json2))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* this covers true, false and null as they are singletons */
|
||||||
|
if(json1 == json2)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if(json_is_object(json1))
|
||||||
|
return json_object_equal(json1, json2);
|
||||||
|
|
||||||
|
if(json_is_array(json1))
|
||||||
|
return json_array_equal(json1, json2);
|
||||||
|
|
||||||
|
if(json_is_string(json1))
|
||||||
|
return json_string_equal(json1, json2);
|
||||||
|
|
||||||
|
if(json_is_integer(json1))
|
||||||
|
return json_integer_equal(json1, json2);
|
||||||
|
|
||||||
|
if(json_is_real(json1))
|
||||||
|
return json_real_equal(json1, json2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
1
test/.gitignore
vendored
1
test/.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
logs
|
logs
|
||||||
bin/json_process
|
bin/json_process
|
||||||
suites/api/test_array
|
suites/api/test_array
|
||||||
|
suites/api/test_equal
|
||||||
suites/api/test_load
|
suites/api/test_load
|
||||||
suites/api/test_number
|
suites/api/test_number
|
||||||
suites/api/test_object
|
suites/api/test_object
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
check_PROGRAMS = \
|
check_PROGRAMS = \
|
||||||
test_array \
|
test_array \
|
||||||
|
test_equal \
|
||||||
test_load \
|
test_load \
|
||||||
test_simple \
|
test_simple \
|
||||||
test_number \
|
test_number \
|
||||||
|
@ -48,6 +48,7 @@ json_dump_file
|
|||||||
json_loads
|
json_loads
|
||||||
json_loadf
|
json_loadf
|
||||||
json_load_file
|
json_load_file
|
||||||
|
json_equal
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# The list of functions are not exported in the library because they
|
# The list of functions are not exported in the library because they
|
||||||
|
187
test/suites/api/test_equal.c
Normal file
187
test/suites/api/test_equal.c
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2009 Petri Lehtinen <petri@digip.org>
|
||||||
|
*
|
||||||
|
* Jansson is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT license. See LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <jansson.h>
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static void test_equal_simple()
|
||||||
|
{
|
||||||
|
json_t *value1, *value2;
|
||||||
|
|
||||||
|
if(json_equal(NULL, NULL))
|
||||||
|
fail("json_equal fails for two NULLs");
|
||||||
|
|
||||||
|
value1 = json_true();
|
||||||
|
if(json_equal(value1, NULL) || json_equal(NULL, value1))
|
||||||
|
fail("json_equal fails for NULL");
|
||||||
|
|
||||||
|
/* this covers true, false and null as they are singletons */
|
||||||
|
if(!json_equal(value1, value1))
|
||||||
|
fail("identical objects are not equal");
|
||||||
|
json_decref(value1);
|
||||||
|
|
||||||
|
/* integer */
|
||||||
|
value1 = json_integer(1);
|
||||||
|
value2 = json_integer(1);
|
||||||
|
if(!value1 || !value2)
|
||||||
|
fail("unable to create integers");
|
||||||
|
if(!json_equal(value1, value2))
|
||||||
|
fail("json_equal fails for two equal integers");
|
||||||
|
json_decref(value2);
|
||||||
|
|
||||||
|
value2 = json_integer(2);
|
||||||
|
if(!value2)
|
||||||
|
fail("unable to create an integer");
|
||||||
|
if(json_equal(value1, value2))
|
||||||
|
fail("json_equal fails for two inequal integers");
|
||||||
|
|
||||||
|
json_decref(value1);
|
||||||
|
json_decref(value2);
|
||||||
|
|
||||||
|
/* real */
|
||||||
|
value1 = json_real(1.2);
|
||||||
|
value2 = json_real(1.2);
|
||||||
|
if(!value1 || !value2)
|
||||||
|
fail("unable to create reals");
|
||||||
|
if(!json_equal(value1, value2))
|
||||||
|
fail("json_equal fails for two equal reals");
|
||||||
|
json_decref(value2);
|
||||||
|
|
||||||
|
value2 = json_real(3.141592);
|
||||||
|
if(!value2)
|
||||||
|
fail("unable to create an real");
|
||||||
|
if(json_equal(value1, value2))
|
||||||
|
fail("json_equal fails for two inequal reals");
|
||||||
|
|
||||||
|
json_decref(value1);
|
||||||
|
json_decref(value2);
|
||||||
|
|
||||||
|
/* string */
|
||||||
|
value1 = json_string("foo");
|
||||||
|
value2 = json_string("foo");
|
||||||
|
if(!value1 || !value2)
|
||||||
|
fail("unable to create strings");
|
||||||
|
if(!json_equal(value1, value2))
|
||||||
|
fail("json_equal fails for two equal strings");
|
||||||
|
json_decref(value2);
|
||||||
|
|
||||||
|
value2 = json_string("bar");
|
||||||
|
if(!value2)
|
||||||
|
fail("unable to create an string");
|
||||||
|
if(json_equal(value1, value2))
|
||||||
|
fail("json_equal fails for two inequal strings");
|
||||||
|
|
||||||
|
json_decref(value1);
|
||||||
|
json_decref(value2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_equal_array()
|
||||||
|
{
|
||||||
|
json_t *array1, *array2;
|
||||||
|
|
||||||
|
array1 = json_array();
|
||||||
|
array2 = json_array();
|
||||||
|
if(!array1 || !array2)
|
||||||
|
fail("unable to create arrays");
|
||||||
|
|
||||||
|
if(!json_equal(array1, array2))
|
||||||
|
fail("json_equal fails for two empty arrays");
|
||||||
|
|
||||||
|
json_array_append_new(array1, json_integer(1));
|
||||||
|
json_array_append_new(array2, json_integer(1));
|
||||||
|
json_array_append_new(array1, json_string("foo"));
|
||||||
|
json_array_append_new(array2, json_string("foo"));
|
||||||
|
json_array_append_new(array1, json_integer(2));
|
||||||
|
json_array_append_new(array2, json_integer(2));
|
||||||
|
if(!json_equal(array1, array2))
|
||||||
|
fail("json_equal fails for two equal arrays");
|
||||||
|
|
||||||
|
json_array_remove(array2, 2);
|
||||||
|
if(json_equal(array1, array2))
|
||||||
|
fail("json_equal fails for two inequal arrays");
|
||||||
|
|
||||||
|
json_array_append_new(array2, json_integer(3));
|
||||||
|
if(json_equal(array1, array2))
|
||||||
|
fail("json_equal fails for two inequal arrays");
|
||||||
|
|
||||||
|
json_decref(array1);
|
||||||
|
json_decref(array2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_equal_object()
|
||||||
|
{
|
||||||
|
json_t *object1, *object2;
|
||||||
|
|
||||||
|
object1 = json_object();
|
||||||
|
object2 = json_object();
|
||||||
|
if(!object1 || !object2)
|
||||||
|
fail("unable to create objects");
|
||||||
|
|
||||||
|
if(!json_equal(object1, object2))
|
||||||
|
fail("json_equal fails for two empty objects");
|
||||||
|
|
||||||
|
json_object_set_new(object1, "a", json_integer(1));
|
||||||
|
json_object_set_new(object2, "a", json_integer(1));
|
||||||
|
json_object_set_new(object1, "b", json_string("foo"));
|
||||||
|
json_object_set_new(object2, "b", json_string("foo"));
|
||||||
|
json_object_set_new(object1, "c", json_integer(2));
|
||||||
|
json_object_set_new(object2, "c", json_integer(2));
|
||||||
|
if(!json_equal(object1, object2))
|
||||||
|
fail("json_equal fails for two equal objects");
|
||||||
|
|
||||||
|
json_object_del(object2, "c");
|
||||||
|
if(json_equal(object1, object2))
|
||||||
|
fail("json_equal fails for two inequal objects");
|
||||||
|
|
||||||
|
json_object_set(object2, "c", json_integer(3));
|
||||||
|
if(json_equal(object1, object2))
|
||||||
|
fail("json_equal fails for two inequal objects");
|
||||||
|
|
||||||
|
json_object_del(object2, "c");
|
||||||
|
json_object_set(object2, "d", json_integer(2));
|
||||||
|
if(json_equal(object1, object2))
|
||||||
|
fail("json_equal fails for two inequal objects");
|
||||||
|
|
||||||
|
json_decref(object1);
|
||||||
|
json_decref(object2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_equal_complex()
|
||||||
|
{
|
||||||
|
json_t *value1, *value2;
|
||||||
|
|
||||||
|
const char *complex_json =
|
||||||
|
"{"
|
||||||
|
" \"integer\": 1, "
|
||||||
|
" \"real\": 3.141592, "
|
||||||
|
" \"string\": \"foobar\", "
|
||||||
|
" \"true\": true, "
|
||||||
|
" \"object\": {"
|
||||||
|
" \"array-in-object\": [1,true,\"foo\",{}],"
|
||||||
|
" \"object-in-object\": {\"foo\": \"bar\"}"
|
||||||
|
" },"
|
||||||
|
" \"array\": [\"foo\", false, null, 1.234]"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
value1 = json_loads(complex_json, NULL);
|
||||||
|
value2 = json_loads(complex_json, NULL);
|
||||||
|
if(!value1 || !value2)
|
||||||
|
fail("unable to parse JSON");
|
||||||
|
if(!json_equal(value1, value2))
|
||||||
|
fail("json_equal fails for two inequal strings");
|
||||||
|
|
||||||
|
/* TODO: There's no negative test case here */
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
test_equal_simple();
|
||||||
|
test_equal_array();
|
||||||
|
test_equal_object();
|
||||||
|
test_equal_complex();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user