mirror of
https://github.com/DaveGamble/cJSON.git
synced 2025-01-14 19:48:00 +08:00
commit
e0a3c2370d
163
cJSON.c
163
cJSON.c
@ -70,25 +70,19 @@ CJSON_PUBLIC(const char*) cJSON_Version(void)
|
||||
return version;
|
||||
}
|
||||
|
||||
/* case insensitive strcmp */
|
||||
static int cJSON_strcasecmp(const unsigned char *string1, const unsigned char *string2)
|
||||
/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
|
||||
static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
|
||||
{
|
||||
if (string1 == NULL)
|
||||
if ((string1 == NULL) || (string2 == NULL))
|
||||
{
|
||||
if (string2 == NULL)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (string1 == string2)
|
||||
{
|
||||
/* both NULL */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (string2 == NULL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
|
||||
{
|
||||
if (*string1 == '\0')
|
||||
@ -97,7 +91,7 @@ static int cJSON_strcasecmp(const unsigned char *string1, const unsigned char *s
|
||||
}
|
||||
}
|
||||
|
||||
return tolower(string1[0]) - tolower(string2[0]);
|
||||
return tolower(*string1) - tolower(*string2);
|
||||
}
|
||||
|
||||
typedef struct internal_hooks
|
||||
@ -1683,34 +1677,44 @@ CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item)
|
||||
return c;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string)
|
||||
{
|
||||
cJSON *c = object ? object->child : NULL;
|
||||
while (c && cJSON_strcasecmp((unsigned char*)c->string, (const unsigned char*)string))
|
||||
{
|
||||
c = c->next;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
|
||||
static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
|
||||
{
|
||||
cJSON *current_element = NULL;
|
||||
|
||||
if ((object == NULL) || (string == NULL))
|
||||
if ((object == NULL) || (name == NULL))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
current_element = object->child;
|
||||
while ((current_element != NULL) && (strcmp(string, current_element->string) != 0))
|
||||
if (case_sensitive)
|
||||
{
|
||||
while ((current_element != NULL) && (strcmp(name, current_element->string) != 0))
|
||||
{
|
||||
current_element = current_element->next;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
|
||||
{
|
||||
current_element = current_element->next;
|
||||
}
|
||||
}
|
||||
|
||||
return current_element;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string)
|
||||
{
|
||||
return get_object_item(object, string, false);
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
|
||||
{
|
||||
return get_object_item(object, string, true);
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
|
||||
{
|
||||
return cJSON_GetObjectItem(object, string) ? 1 : 0;
|
||||
@ -1860,7 +1864,7 @@ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *stri
|
||||
{
|
||||
size_t i = 0;
|
||||
cJSON *c = object->child;
|
||||
while (c && cJSON_strcasecmp((unsigned char*)c->string, (const unsigned char*)string))
|
||||
while (c && (case_insensitive_strcmp((unsigned char*)c->string, (const unsigned char*)string) != 0))
|
||||
{
|
||||
i++;
|
||||
c = c->next;
|
||||
@ -1948,7 +1952,7 @@ CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string,
|
||||
{
|
||||
size_t i = 0;
|
||||
cJSON *c = object->child;
|
||||
while(c && cJSON_strcasecmp((unsigned char*)c->string, (const unsigned char*)string))
|
||||
while(c && (case_insensitive_strcmp((unsigned char*)c->string, (const unsigned char*)string) != 0))
|
||||
{
|
||||
i++;
|
||||
c = c->next;
|
||||
@ -2480,3 +2484,104 @@ CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
|
||||
|
||||
return (item->type & 0xFF) == cJSON_Raw;
|
||||
}
|
||||
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
|
||||
{
|
||||
if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* check if type is valid */
|
||||
switch (a->type & 0xFF)
|
||||
{
|
||||
case cJSON_False:
|
||||
case cJSON_True:
|
||||
case cJSON_NULL:
|
||||
case cJSON_Number:
|
||||
case cJSON_String:
|
||||
case cJSON_Raw:
|
||||
case cJSON_Array:
|
||||
case cJSON_Object:
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
/* identical objects are equal */
|
||||
if (a == b)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (a->type & 0xFF)
|
||||
{
|
||||
/* in these cases and equal type is enough */
|
||||
case cJSON_False:
|
||||
case cJSON_True:
|
||||
case cJSON_NULL:
|
||||
return true;
|
||||
|
||||
case cJSON_Number:
|
||||
if (a->valuedouble == b->valuedouble)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case cJSON_String:
|
||||
case cJSON_Raw:
|
||||
if ((a->valuestring == NULL) || (b->valuestring == NULL))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (strcmp(a->valuestring, b->valuestring) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case cJSON_Array:
|
||||
{
|
||||
cJSON *a_element = NULL;
|
||||
cJSON *b_element = NULL;
|
||||
for (a_element = a->child, b_element = b->child;
|
||||
(a_element != NULL) && (b_element != NULL);
|
||||
a_element = a_element->next, b_element = b_element->next)
|
||||
{
|
||||
if (!cJSON_Compare(a_element, b_element, case_sensitive))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case cJSON_Object:
|
||||
{
|
||||
cJSON *a_element = NULL;
|
||||
cJSON_ArrayForEach(a_element, a)
|
||||
{
|
||||
/* TODO This has O(n^2) runtime, which is horrible! */
|
||||
cJSON *b_element = get_object_item(b, a_element->string, case_sensitive);
|
||||
if (b_element == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cJSON_Compare(a_element, b_element, case_sensitive))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
4
cJSON.h
4
cJSON.h
@ -212,6 +212,10 @@ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
|
||||
/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
|
||||
need to be released. With recurse!=0, it will duplicate any children connected to the item.
|
||||
The item->next and ->prev pointers are always zero on return from Duplicate. */
|
||||
/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
|
||||
* case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
|
||||
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
|
||||
|
||||
|
||||
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
|
||||
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
|
||||
|
@ -46,6 +46,7 @@ if(ENABLE_CJSON_TEST)
|
||||
print_value
|
||||
misc_tests
|
||||
parse_with_opts
|
||||
compare_tests
|
||||
)
|
||||
|
||||
add_library(test-common common.c)
|
||||
|
192
tests/compare_tests.c
Normal file
192
tests/compare_tests.c
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "unity/examples/unity_config.h"
|
||||
#include "unity/src/unity.h"
|
||||
#include "common.h"
|
||||
|
||||
static cJSON_bool compare_from_string(const char * const a, const char * const b, const cJSON_bool case_sensitive)
|
||||
{
|
||||
cJSON *a_json = NULL;
|
||||
cJSON *b_json = NULL;
|
||||
cJSON_bool result = false;
|
||||
|
||||
a_json = cJSON_Parse(a);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(a_json, "Failed to parse a.");
|
||||
b_json = cJSON_Parse(b);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(b_json, "Failed to parse b.");
|
||||
|
||||
result = cJSON_Compare(a_json, b_json, case_sensitive);
|
||||
|
||||
cJSON_Delete(a_json);
|
||||
cJSON_Delete(b_json);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_null_pointer_as_not_equal(void)
|
||||
{
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(NULL, NULL, true));
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(NULL, NULL, false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_invalid_as_not_equal(void)
|
||||
{
|
||||
cJSON invalid[1];
|
||||
memset(invalid, '\0', sizeof(invalid));
|
||||
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, false));
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, true));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_numbers(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("1", "1", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("1", "1", false));
|
||||
TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("0.0001", "0.0001", false));
|
||||
|
||||
TEST_ASSERT_FALSE(compare_from_string("1", "2", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("1", "2", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_booleans(void)
|
||||
{
|
||||
/* true */
|
||||
TEST_ASSERT_TRUE(compare_from_string("true", "true", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("true", "true", false));
|
||||
|
||||
/* false */
|
||||
TEST_ASSERT_TRUE(compare_from_string("false", "false", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("false", "false", false));
|
||||
|
||||
/* mixed */
|
||||
TEST_ASSERT_FALSE(compare_from_string("true", "false", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("true", "false", false));
|
||||
TEST_ASSERT_FALSE(compare_from_string("false", "true", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("false", "true", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_null(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("null", "null", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("null", "null", false));
|
||||
|
||||
TEST_ASSERT_FALSE(compare_from_string("null", "true", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("null", "true", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_not_accept_invalid_types(void)
|
||||
{
|
||||
cJSON invalid[1];
|
||||
memset(invalid, '\0', sizeof(invalid));
|
||||
|
||||
invalid->type = cJSON_Number | cJSON_String;
|
||||
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, true));
|
||||
TEST_ASSERT_FALSE(cJSON_Compare(invalid, invalid, false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_strings(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("\"abcdefg\"", "\"abcdefg\"", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("\"abcdefg\"", "\"abcdefg\"", false));
|
||||
|
||||
TEST_ASSERT_FALSE(compare_from_string("\"ABCDEFG\"", "\"abcdefg\"", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("\"ABCDEFG\"", "\"abcdefg\"", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_raw(void)
|
||||
{
|
||||
cJSON *raw1 = NULL;
|
||||
cJSON *raw2 = NULL;
|
||||
|
||||
raw1 = cJSON_Parse("\"[true, false]\"");
|
||||
TEST_ASSERT_NOT_NULL(raw1);
|
||||
raw2 = cJSON_Parse("\"[true, false]\"");
|
||||
TEST_ASSERT_NOT_NULL(raw2);
|
||||
|
||||
raw1->type = cJSON_Raw;
|
||||
raw2->type = cJSON_Raw;
|
||||
|
||||
TEST_ASSERT_TRUE(cJSON_Compare(raw1, raw2, true));
|
||||
TEST_ASSERT_TRUE(cJSON_Compare(raw1, raw2, false));
|
||||
|
||||
cJSON_Delete(raw1);
|
||||
cJSON_Delete(raw2);
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_arrays(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("[]", "[]", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("[]", "[]", false));
|
||||
|
||||
TEST_ASSERT_TRUE(compare_from_string("[false,true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("[false,true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", false));
|
||||
|
||||
TEST_ASSERT_TRUE(compare_from_string("[[[1], 2]]", "[[[1], 2]]", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("[[[1], 2]]", "[[[1], 2]]", false));
|
||||
|
||||
TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", true));
|
||||
TEST_ASSERT_FALSE(compare_from_string("[true,null,42,\"string\",[],{}]", "[false, true, null, 42, \"string\", [], {}]", false));
|
||||
}
|
||||
|
||||
static void cjson_compare_should_compare_objects(void)
|
||||
{
|
||||
TEST_ASSERT_TRUE(compare_from_string("{}", "{}", true));
|
||||
TEST_ASSERT_TRUE(compare_from_string("{}", "{}", false));
|
||||
|
||||
TEST_ASSERT_TRUE(compare_from_string(
|
||||
"{\"false\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
true));
|
||||
TEST_ASSERT_FALSE(compare_from_string(
|
||||
"{\"False\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
true));
|
||||
TEST_ASSERT_TRUE(compare_from_string(
|
||||
"{\"False\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
false));
|
||||
TEST_ASSERT_FALSE(compare_from_string(
|
||||
"{\"Flse\": false, \"true\": true, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
"{\"true\": true, \"false\": false, \"null\": null, \"number\": 42, \"string\": \"string\", \"array\": [], \"object\": {}}",
|
||||
false));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
||||
RUN_TEST(cjson_compare_should_compare_null_pointer_as_not_equal);
|
||||
RUN_TEST(cjson_compare_should_compare_invalid_as_not_equal);
|
||||
RUN_TEST(cjson_compare_should_compare_numbers);
|
||||
RUN_TEST(cjson_compare_should_compare_booleans);
|
||||
RUN_TEST(cjson_compare_should_compare_null);
|
||||
RUN_TEST(cjson_compare_should_not_accept_invalid_types);
|
||||
RUN_TEST(cjson_compare_should_compare_strings);
|
||||
RUN_TEST(cjson_compare_should_compare_raw);
|
||||
RUN_TEST(cjson_compare_should_compare_arrays);
|
||||
RUN_TEST(cjson_compare_should_compare_objects);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user