文章目录
json简介
JSON(JavaScript Object Notation) 是一种轻量级的存储和交换文本信息格式。
JSON 比 XML 更小、更快,更易解析。
JSON 数据的书写格式是:key : value
。
JSON 值可以是:
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true 或 false)
- 数组(在中括号中)
- 对象(在大括号中)(因为这是c语言实现的可以当作结构体)
- null
其中对象可以包含多个名称/值对。
cjson中的基本类型
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
其中:
类型 | 信息 |
---|---|
cJSON_Invalid | 无效的节点 |
cJSON_False | 逻辑假节点 |
cJSON_True | 逻辑真节点 |
cJSON_Number | 数字节点 |
cJSON_String | 字符串节点 |
cJSON_Array | 数组节点 |
cJSON_Object | 对象节点 |
cJSON_Raw | 原始json节点(本质就是字符串节点,只不过字符串存储的是json) |
cJSON_IsReference | 子节点指向的child/valuestring不属于此项目,只是一个引用。cJSON_Delete和其他函数只会解除分配这个项目,而不释放child/valuestring。 |
cJSON_StringIsConst | 该string指向一个常量string。表明cJSON_Delete和其他函数不能尝试释放该string。 |
cJSON_IsReference 和cJSON_StringIsConst 的值的设置可以让这两个类型和其他的基础类型共存(用位运算)。
json结构体
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;
其中:
- *next,*prev是想做成节点双链表(为了快速找到尾节点,首节点的prev指向尾节点)
- *child是指向子节点的,子节点只有数组和对象有
- type就是cjson中的类型
- valuestring是字符串节点的值
- valueint和valuedouble是数值类型的值
- string为节点的key也就是对象名
版本管理
头文件版本信息:
/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 14
源文件版本匹配:
/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 14)
#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
#endif
获取版本字符串(用来打印):
CJSON_PUBLIC(const char*) cJSON_Version(void)
{
static char version[15];
sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
return version;
}
内存管理
cjson 使用 Hook 技术来让使用者可以自定义内存管理函数。
typedef struct cJSON_Hooks
{
/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
void *(CJSON_CDECL *malloc_fn)(size_t sz);
void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
默认内存管理就是直接或者间接使用malloc 、 free和realloc。
typedef struct internal_hooks
{
void *(CJSON_CDECL *allocate)(size_t size);
void (CJSON_CDECL *deallocate)(void *pointer);
void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
} internal_hooks;
#if defined(_MSC_VER)
/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
static void * CJSON_CDECL internal_malloc(size_t size)
{
return malloc(size);
}
static void CJSON_CDECL internal_free(void *pointer)
{
free(pointer);
}
static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
{
return realloc(pointer, size);
}
#else
#define internal_malloc malloc
#define internal_free free
#define internal_realloc realloc
#endif
static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
cJSON_InitHooks 参数为空默认使用系统的 malloc 、 free和realloc 函数,
cJSON_InitHooks 参数不为空则替换成用户自定义的 malloc 和 free 函数,并且不使用realloc函数。
/* Supply malloc, realloc and free functions to cJSON */
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
{
// 使用默认的malloc 、 free和realloc
if (hooks == NULL)
{
/* Reset hooks */
global_hooks.allocate = malloc;
global_hooks.deallocate = free;
global_hooks.reallocate = realloc;
return;
}
// 设置自己的malloc
global_hooks.allocate = malloc;
if (hooks->malloc_fn != NULL)
{
global_hooks.allocate = hooks->malloc_fn;
}
// 设置自己的free
global_hooks.deallocate = free;
if (hooks->free_fn != NULL)
{
global_hooks.deallocate = hooks->free_fn;
}
/* use realloc only if both free and malloc are used */
// 只有当使用系统的free and malloc时候才设置realloc
global_hooks.reallocate = NULL;
if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
{
global_hooks.reallocate = realloc;
}
}
使用设置好的内存管理函数:
/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
{
return global_hooks.allocate(size);
}
CJSON_PUBLIC(void) cJSON_free(void *object)
{
global_hooks.deallocate(object);
}
创建和释放节点
申请节点内存
申请空间并且初始化为0。
/* Internal constructor. */
static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
{
cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
if (node)
{
memset(node, '\0', sizeof(cJSON));// 结构体初始化为全0
}
return node;
}
创建具体节点
1、创建NULL类型、True类型、False类型、True或False类型节点:
/* Create basic types: */
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
{
cJSON *item = cJSON_New_Item(&global_hooks);
if (item)
{
item->type = cJSON_NULL;
}
return item;
}
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
2、创建数值类型节点(数值类型不细分整形和浮点型,在设置整形的时候对上溢出做了判断):
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
{
cJSON *item = cJSON_New_Item(&global_hooks);
if (item)
{
item->type = cJSON_Number;
item->valuedouble = num;
/* use saturation in case of overflow */
if (num >= INT_MAX)
{
item->valueint = INT_MAX;
}
else if (num <= (double)INT_MIN)
{
item->valueint = INT_MIN;
}
else
{
item->valueint = (int)num;
}
}
return item;
}
3、创建字符串类型和原始json类型节点(valuestring是动态分配内存的):
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
{
cJSON *item = cJSON_New_Item(&global_hooks);
if (item)
{
item->type = cJSON_String;
item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
if (!item->valuestring)
{
cJSON_Delete(item);
return NULL;
}
}
return item;
}
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
其中用到了cJSON_strdup可以看出来是一个字符串的深拷贝:
static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
{
size_t length = 0;
unsigned char *copy = NULL;
if (string == NULL)
{
return NULL;
}
// strlen不会计算字符串结束符\0 所以需要加上一个字节
length = strlen((const char*)string) + sizeof("");
// 申请内存并且拷贝
copy = (unsigned char*)hooks->allocate(length);
if (copy == NULL)
{
return NULL;
}
memcpy(copy, string, length);
return copy;
}
4、创建数组类型和对象类型节点(具体内容都在子节点中):
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
{
cJSON *item = cJSON_New_Item(&global_hooks);
if (item)
{
item->type = cJSON_Array;
}
return item;
}
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
5、创建引用类型节点:
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
{
cJSON *item = cJSON_New_Item(&global_hooks);
if (item != NULL)
{
item->type = cJSON_String | cJSON_IsReference;
item->valuestring = (char*)cast_away_const(string);
}
return item;
}
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
其中用到了cast_away_const用来去除指针的const属性:
/* helper function to cast away const */
static void* cast_away_const(const void* string)
{
return (void*)string;
}
6、还提供了四种数组的创建(除了节点类型不一样,其他都差不多),一个数组就是一个双链表:
/* Create Arrays: */
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
{
size_t i = 0;
cJSON *n = NULL;
cJSON *p = NULL;
cJSON *a = NULL;
if ((count < 0) || (numbers == NULL))
{
return NULL;
}
a = cJSON_CreateArray();
for (i = 0; a && (i < (size_t)count); i++)
{
n = cJSON_CreateNumber(numbers[i]);
if (!n)
{
cJSON_Delete(a);
return NULL;
}
if (!i)
{
a->child = n;
}
else
{
suffix_object(p, n);
}
p = n;
}
// 为了快速找到尾节点,首节点的prev指向尾节点
if (a && a->child)
{
a->child->prev = n;
}
return a;
}
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
其中suffix_object是用来连接前后两个节点的:
/* Utility for array list handling. */
static void suffix_object(cJSON *prev, cJSON *item)
{
prev->next = item;
item->prev = prev;
}
复制节点
recurse控制是否复制子节点。
/* Duplication */
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
{
cJSON *newitem = NULL;
cJSON *child = NULL;
cJSON *next = NULL;
cJSON *newchild = NULL;
/* Bail on bad ptr */
if (!item)
{
goto fail;
}
/* Create new item */
// 申请空间
newitem = cJSON_New_Item(&global_hooks);
if (!newitem)
{
goto fail;
}
/* Copy over all vars */
// 复制所有变量
newitem->type = item->type & (~cJSON_IsReference);
newitem->valueint = item->valueint;
newitem->valuedouble = item->valuedouble;
if (item->valuestring)
{
newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
if (!newitem->valuestring)
{
goto fail;
}
}
if (item->string)
{
newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
if (!newitem->string)
{
goto fail;
}
}
/* If non-recursive, then we're done! */
if (!recurse)
{
return newitem;
}
/* Walk the ->next chain for the child. */
// 复制子节点
child = item->child;
while (child != NULL)
{
newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
if (!newchild)
{
goto fail;
}
if (next != NULL)
{
/* If newitem->child already set, then crosswire ->prev and ->next and move on */
next->next = newchild;
newchild->prev = next;
next = newchild;
}
else
{
/* Set newitem->child and move to it */
newitem->child = newchild;
next = newchild;
}
child = child->next;
}
if (newitem && newitem->child)
{
newitem->child->prev = newchild;
}
return newitem;
// 发生异常
fail:
if (newitem != NULL)
{
cJSON_Delete(newitem);
}
return NULL;
}
释放节点
如果没有cJSON_IsReference标志并且有子节点则递归释放子节点,
如果没有cJSON_IsReference标志并且有字符串资源则释放字符串,
如果没有cJSON_StringIsConst标志并且有key资源则释放key,
释放自身节点,如果有next节点则进入下一次循环,释放下一个节点。
/* Delete a cJSON structure. */
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
{
cJSON *next = NULL;
while (item != NULL)
{
next = item->next;
if (!(item->type & cJSON_IsReference) && (item->child != NULL))
{
cJSON_Delete(item->child);// 不是引用节点并且有子节点则递归释放子节点
}
if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
{
global_hooks.deallocate(item->valuestring);// 不是引用节点并且有字符串资源则释放字符串
}
if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
{
global_hooks.deallocate(item->string);// 不是const的key并且有key资源则释key
}
global_hooks.deallocate(item);// 释放节点
item = next;
}
}
判断节点类型
item->type & 0xFF去掉cJSON_IsReference和cJSON_StringIsConst属性。
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
{
if (item == NULL)
{
return false;
}
return (item->type & 0xFF) == cJSON_Invalid;
}
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
// 比较两个节点是否一样
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
{
// 无效的节点、类型不一样的节点、null不是同一个节点
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 (compare_double(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 = a->child;
cJSON *b_element = b->child;
// 循环比较每一个子节点 顺序有关
for (; (a_element != NULL) && (b_element != NULL);)
{
if (!cJSON_Compare(a_element, b_element, case_sensitive))
{
return false;
}
a_element = a_element->next;
b_element = b_element->next;
}
/* one of the arrays is longer than the other */
if (a_element != b_element)
{
return false;
}
return true;
}
case cJSON_Object:
{
cJSON *a_element = NULL;
cJSON *b_element = NULL;
// 循环a的子节点 判断b中是否有相同的子节点 顺序无关
cJSON_ArrayForEach(a_element, a)
{
/* TODO This has O(n^2) runtime, which is horrible! */
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;
}
}
/* doing this twice, once on a and b to prevent true comparison if a subset of b
* TODO: Do this the proper way, this is just a fix for now */
// 循环b的子节点 判断a中是否有相同的子节点 顺序无关
cJSON_ArrayForEach(b_element, b)
{
a_element = get_object_item(a, b_element->string, case_sensitive);
if (a_element == NULL)
{
return false;
}
if (!cJSON_Compare(b_element, a_element, case_sensitive))
{
return false;
}
}
return true;
}
default:
return false;
}
}
得到节点的字符串、数值
// 如果是cJSON_String类型获取其字符串地址
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
{
if (!cJSON_IsString(item))
{
return NULL;
}
return item->valuestring;
}
// 如果是cJSON_Number类型获取其浮点值
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
{
if (!cJSON_IsNumber(item))
{
return (double)NAN;
}
return item->valuedouble;
}
设置节点的字符串、数值
/* When assigning an integer value, it needs to be propagated to valuedouble too. */
// 用整数设置节点数值
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
// 用浮点数设置节点数值
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
// 帮助设置数字节点的值
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
{
// 处理int上下溢出问题
if (number >= INT_MAX)
{
object->valueint = INT_MAX;
}
else if (number <= (double)INT_MIN)
{
object->valueint = INT_MIN;
}
else
{
object->valueint = (int)number;
}
return object->valuedouble = number;
}
// 设置字符串节点字符串值
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
{
char *copy = NULL;
/* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference))
{
return NULL;
}
// 如果原来的字符串比要设置的字符串长 直接浅拷贝
if (strlen(valuestring) <= strlen(object->valuestring))
{
strcpy(object->valuestring, valuestring);
return object->valuestring;
}
// 深拷贝字符串 释放原来的字符串
copy = (char*)cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
if (copy == NULL)
{
return NULL;
}
if (object->valuestring != NULL)
{
cJSON_free(object->valuestring);
}
object->valuestring = copy;
return copy;
}
节点 增删改查
增加节点
1、给数组添加节点,在数组子节点链表最后添加节点。
/* Add item to array/object. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
{
return add_item_to_array(array, item);
}
static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
{
cJSON *child = NULL;
if ((item == NULL) || (array == NULL) || (array == item))
{
return false;
}
child = array->child;
/*
* To find the last item in array quickly, we use prev in array
*/
if (child == NULL)
{
/* list is empty, start new one */
array->child = item;
item->prev = item;
item->next = NULL;
}
else
{
/* append to the end */
if (child->prev)
{
suffix_object(child->prev, item);
array->child->prev = item;
}
}
return true;
}
2、给对象添加节点,先设置要添加节点的key值(使用深拷贝),再添加到子节点链表的最后。cs版本不同的是key值是常量字符串的引用(不需要深拷贝,直接使用,所以也不会去释放)。也是在子节点链表最后添加节点。
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
return add_item_to_object(object, string, item, &global_hooks, false);
}
/* Add an item to an object with constant string as key */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
{
return add_item_to_object(object, string, item, &global_hooks, true);
}
static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
{
char *new_key = NULL;
int new_type = cJSON_Invalid;
if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
{
return false;
}
if (constant_key)
{
new_key = (char*)cast_away_const(string);
new_type = item->type | cJSON_StringIsConst;
}
else
{
new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
if (new_key == NULL)
{
return false;
}
new_type = item->type & ~cJSON_StringIsConst;
}
if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
{
hooks->deallocate(item->string);
}
item->string = new_key;
item->type = new_type;
return add_item_to_array(object, item);
}
3、给数组/对象添加引用节点
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
{
if (array == NULL)
{
return false;
}
return add_item_to_array(array, create_reference(item, &global_hooks));
}
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
{
if ((object == NULL) || (string == NULL))
{
return false;
}
return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
}
其中create_reference完全复制一个节点后,string next prevs设置为空,添加cJSON_IsReference标志,也就是说valuestring和child还是原来节点的资源。
因为当我们要添加的节点已经在一个树上的时候, 再向另一个树中添加这个节点时, 这个节点的 pre 和 next 指针会被覆盖。
/* Utility for handling references. */
static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
{
cJSON *reference = NULL;
if (item == NULL)
{
return NULL;
}
reference = cJSON_New_Item(hooks);
if (reference == NULL)
{
return NULL;
}
memcpy(reference, item, sizeof(cJSON));
reference->string = NULL;
reference->type |= cJSON_IsReference;
reference->next = reference->prev = NULL;
return reference;
}
4、快速构建对象的操作:
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
{
cJSON *null = cJSON_CreateNull();
if (add_item_to_object(object, name, null, &global_hooks, false))
{
return null;
}
cJSON_Delete(null);
return NULL;
}
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
删除节点
1、从数组中移除节点,就是双链表删除节点。移除的节点要记得释放。
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
{
cJSON_Delete(cJSON_DetachItemFromArray(array, which));
}
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
{
if (which < 0)
{
return NULL;
}
return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
}
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
{
if ((parent == NULL) || (item == NULL))
{
return NULL;
}
if (item != parent->child)
{
/* not the first element */
item->prev->next = item->next;
}
if (item->next != NULL)
{
/* not the last element */
item->next->prev = item->prev;
}
if (item == parent->child)
{
/* first element */
parent->child = item->next;
}
else if (item->next == NULL)
{
/* last element */
parent->child->prev = item->prev;
}
/* make sure the detached item doesn't point anywhere anymore */
item->prev = NULL;
item->next = NULL;
return item;
}
其中用到了get_array_item来获取子节点地址(通过索引得到):
static cJSON* get_array_item(const cJSON *array, size_t index)
{
cJSON *current_child = NULL;
if (array == NULL)
{
return NULL;
}
current_child = array->child;
while ((current_child != NULL) && (index > 0))
{
index--;
current_child = current_child->next;
}
return current_child;
}
2、从对象中移除节点,和数组一样是双链表删除节点。比较key的时候不区分大小写。移除的节点要记得释放。
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
{
cJSON_Delete(cJSON_DetachItemFromObject(object, string));
}
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
{
cJSON *to_detach = cJSON_GetObjectItem(object, string);
return cJSON_DetachItemViaPointer(object, to_detach);
}
其中用到了cJSON_GetObjectItem来获取子节点地址(case_insensitive_strcmp是不区分大小写的字符串比较函数):
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
{
return get_object_item(object, string, false);
}
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) || (name == NULL))
{
return NULL;
}
current_element = object->child;
if (case_sensitive)
{
while ((current_element != NULL) && (current_element->string != 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;
}
}
if ((current_element == NULL) || (current_element->string == NULL))
{
return NULL;
}
return current_element;
}
/* 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) || (string2 == NULL))
{
return 1;
}
if (string1 == string2)
{
return 0;
}
// tolower把字母字符转换成小写
for (; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
{
if (*string1 == '\0')
{
return 0;
}
}
return tolower(*string1) - tolower(*string2);
}
3、从对象中移除节点,比较key的时候区分大小写。
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
{
cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
}
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
{
cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
return cJSON_DetachItemViaPointer(object, to_detach);
}
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
{
return get_object_item(object, string, true);
}
查找节点
1、查找数组的节点,简单的数组遍历。
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
{
if (index < 0)
{
return NULL;
}
return get_array_item(array, (size_t)index);
}
static cJSON* get_array_item(const cJSON *array, size_t index)
{
cJSON *current_child = NULL;
if (array == NULL)
{
return NULL;
}
current_child = array->child;
while ((current_child != NULL) && (index > 0))
{
index--;
current_child = current_child->next;
}
return current_child;
}
2、查找对象的节点,比较key的时候不区分大小写:
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
{
return get_object_item(object, string, false);
}
3、查找对象的节点,比较key的时候区分大小写:
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
{
return get_object_item(object, string, true);
}
4、判断对象是否有某个子节点:
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
{
return cJSON_GetObjectItem(object, string) ? 1 : 0;
}
5、计算子节点数量:
/* Get Array size/item / object item. */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
{
cJSON *child = NULL;
size_t size = 0;
if (array == NULL)
{
return 0;
}
child = array->child;
while (child != NULL)
{
size++;
child = child->next;
}
/* FIXME: Can overflow here. Cannot be fixed without breaking the API */
return (int)size;
}
修改节点
1、修改数组节点,原先的节点被释放了。
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
{
if (which < 0)
{
return false;
}
return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
}
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
{
if ((parent == NULL) || (replacement == NULL) || (item == NULL))
{
return false;
}
if (replacement == item)
{
return true;
}
replacement->next = item->next;
replacement->prev = item->prev;
if (replacement->next != NULL)
{
replacement->next->prev = replacement;
}
if (parent->child == item)
{
if (parent->child->prev == parent->child)
{
replacement->prev = replacement;
}
parent->child = replacement;
}
else
{ /*
* To find the last item in array quickly, we use prev in array.
* We can't modify the last item's next pointer where this item was the parent's child
*/
if (replacement->prev != NULL)
{
replacement->prev->next = replacement;
}
if (replacement->next == NULL)
{
parent->child->prev = replacement;
}
}
item->next = NULL;
item->prev = NULL;
cJSON_Delete(item);
return true;
}
2、修改对象节点,比较key时候不区分大小写:
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
{
return replace_item_in_object(object, string, newitem, false);
}
static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
{
if ((replacement == NULL) || (string == NULL))
{
return false;
}
/* replace the name in the replacement */
if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
{
cJSON_free(replacement->string);
}
replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
replacement->type &= ~cJSON_StringIsConst;
return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
}
3、修改对象节点,比较key时候区分大小写:
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
{
return replace_item_in_object(object, string, newitem, true);
}
4、在指定位置插入节点,如果位置大于链表长度就在最后插入节点。
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
{
cJSON *after_inserted = NULL;
if (which < 0)
{
return false;
}
after_inserted = get_array_item(array, (size_t)which);
if (after_inserted == NULL)
{
return add_item_to_array(array, newitem);// 在尾部插入
}
newitem->next = after_inserted;
newitem->prev = after_inserted->prev;
after_inserted->prev = newitem;
if (after_inserted == array->child)
{
array->child = newitem;
}
else
{
newitem->prev->next = newitem;
}
return true;
}
json字符串解析
json解析器
cJSON_ParseWithOpts的参数return_parse_end判断是否需要返回解析的尾地址,require_null_terminated判断是否以\0结束。
/* Default options for cJSON_Parse */
// 解析整个字符串
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
{
return cJSON_ParseWithOpts(value, 0, 0);
}
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
{
size_t buffer_length;
if (NULL == value)
{
return NULL;
}
/* Adding null character size due to require_null_terminated. */
// 添加\0的长度
buffer_length = strlen(value) + sizeof("");
return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
}
// 解析字符串指定长度
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
{
return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
}
/* Parse an object - create a new root, and populate. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
{
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
cJSON *item = NULL;
/* reset error position */
// 重置错误位置
global_error.json = NULL;
global_error.position = 0;
if (value == NULL || 0 == buffer_length)
{
goto fail;
}
// 设置buffer
buffer.content = (const unsigned char*)value;
buffer.length = buffer_length;
buffer.offset = 0;
buffer.hooks = global_hooks;
// 请求一个节点内存
item = cJSON_New_Item(&global_hooks);
if (item == NULL) /* memory fail */
{
goto fail;
}
// 解析字符串
if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
{
/* parse failure. ep is set. */
goto fail;
}
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
// 检查结尾是否为'\0'
if (require_null_terminated)
{
buffer_skip_whitespace(&buffer);
if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
{
goto fail;
}
}
// 设置结尾地址
if (return_parse_end)
{
*return_parse_end = (const char*)buffer_at_offset(&buffer);
}
return item;
// 发生异常
fail:
// 释放申请的内存
if (item != NULL)
{
cJSON_Delete(item);
}
// 设置全局错误标志
if (value != NULL)
{
error local_error;
local_error.json = (const unsigned char*)value;
local_error.position = 0;
if (buffer.offset < buffer.length)
{
local_error.position = buffer.offset;
}
else if (buffer.length > 0)
{
local_error.position = buffer.length - 1;
}
if (return_parse_end != NULL)
{
*return_parse_end = (const char*)local_error.json + local_error.position;
}
global_error = local_error;
}
return NULL;
}
其中parse_buffer类型把解析需要的信息组装在一起:
// 解析工作区类型
typedef struct
{
const unsigned char *content;// 待解析的字符串
size_t length;// 字符串长度
size_t offset;// 解析偏移
size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
internal_hooks hooks;// 内存管理函数
} parse_buffer;
skip_utf8_bom用来跳过字节序标记(EF BB BF):
/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
{
if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
{
return NULL;
}
if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
{
buffer->offset += 3;
}
return buffer;
}
buffer_skip_whitespace用来跳过空白部分:
/* Utility to jump whitespace and cr/lf */
static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
{
if ((buffer == NULL) || (buffer->content == NULL))
{
return NULL;
}
// 字符串偏移到底了
if (cannot_access_at_index(buffer, 0))
{
return buffer;
}
// 小于32的都是不可打印字符 32是空格 全都跳过
while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
{
buffer->offset++;
}
// 如果偏移到了\0则则回退一个
if (buffer->offset == buffer->length)
{
buffer->offset--;
}
return buffer;
}
函数里的global_error可以用来提供错误信息,指向出错的字符串的位置。
// 错误格式类型
typedef struct
{
const unsigned char *json;
size_t position;
} error;
// 全局的错误记录变量
static error global_error = { NULL, 0 };
// 获取错误信息
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
{
return (const char*)(global_error.json + global_error.position);
}
然后就是parse_value函数了,根据前几个字符来判断写一个类型是什么。如果是 null, false 或 true 设置类型,并返回偏移指针。如果是其他的,则进入对应的处理函数中。
/* Parser core - when encountering text, process appropriately. */
static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
{
if ((input_buffer == NULL) || (input_buffer->content == NULL))
{
return false; /* no input */
}
/* parse the different types of values */
/* null */
if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
{
item->type = cJSON_NULL;
input_buffer->offset += 4;
return true;
}
/* false */
if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
{
item->type = cJSON_False;
input_buffer->offset += 5;
return true;
}
/* true */
if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
{
item->type = cJSON_True;
item->valueint = 1;
input_buffer->offset += 4;
return true;
}
/* string */
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
{
return parse_string(item, input_buffer);
}
/* number */
if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
{
return parse_number(item, input_buffer);
}
/* array */
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
{
return parse_array(item, input_buffer);
}
/* object */
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
{
return parse_object(item, input_buffer);
}
return false;
}
其中用到的宏(提高代码的可读性):
/* check if the given size is left to read in a given parse buffer (starting with 1) */
// 检测对应buffer是否可以往后偏移size个单位
#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
/* check if the buffer can be accessed at the given index (starting with 0) */
// 检测是否可以访问buffer偏移+index位置
#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
/* get a pointer to the buffer at the position */
// 得到待解析的字符串偏移地址
#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
解析字符串
首先是遍历了一次整个string,统计出一共的字符个数,遇到转义的部分跳过了后面的字符不统计为长度(比如遇到了\
和t
两个字符最后会解析成\t
一个转义字符)。
申请足够数量的内存,然后重新从头遍历整个string,特殊处理转义的部分,也包括UTF-16字符,这些字符在字符串中会编码为 \uXXXX 的字符串, 将他它还原为 0-255 的一个字符。
/* Parse the input text into an unescaped cinput, and populate item. */
static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
{
const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
unsigned char *output_pointer = NULL;
unsigned char *output = NULL;
/* not a string */
if (buffer_at_offset(input_buffer)[0] != '\"')
{
goto fail;
}
{
/* calculate approximate size of the output (overestimate) */
size_t allocation_length = 0;
size_t skipped_bytes = 0;// 记录要跳过的转义字符数量
// 遍历一遍字符串统计转义字符数量(直到遇到"结束)
while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
{
/* is escape sequence */
// 遇到转义字符++skipped_bytes
if (input_end[0] == '\\')
{
if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
{
/* prevent buffer overflow when last input character is a backslash */
goto fail;
}
skipped_bytes++;
input_end++;
}
input_end++;
}
if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
{
goto fail; /* string ended unexpectedly */
}
/* This is at most how much we need for the output */
// 分配字符串空间 大小:原字符串长度-转义字符数量
allocation_length = (size_t)(input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
if (output == NULL)
{
goto fail; /* allocation failure */
}
}
output_pointer = output;
/* loop through the string literal */
// 遍历一遍字符串复制内容 处理特殊编码
while (input_pointer < input_end)
{
// 普通字符直接复制
if (*input_pointer != '\\')
{
*output_pointer++ = *input_pointer++;
}
/* escape sequence */
// 以\开头的两个字符解析成一个转义字符
else
{
unsigned char sequence_length = 2;
if ((input_end - input_pointer) < 1)
{
goto fail;
}
switch (input_pointer[1])
{
case 'b':
*output_pointer++ = '\b';
break;
case 'f':
*output_pointer++ = '\f';
break;
case 'n':
*output_pointer++ = '\n';
break;
case 'r':
*output_pointer++ = '\r';
break;
case 't':
*output_pointer++ = '\t';
break;
case '\"':
case '\\':
case '/':
*output_pointer++ = input_pointer[1];
break;
/* UTF-16 literal */
// 处理utf字符
case 'u':
sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
if (sequence_length == 0)
{
/* failed to convert UTF16-literal to UTF-8 */
goto fail;
}
break;
default:
goto fail;
}
input_pointer += sequence_length;
}
}
/* zero terminate the output */
// 设置字符串结尾
*output_pointer = '\0';
item->type = cJSON_String;
item->valuestring = (char*)output;
input_buffer->offset = (size_t)(input_end - input_buffer->content);
input_buffer->offset++;
return true;
// 发生异常
fail:
if (output != NULL)
{
input_buffer->hooks.deallocate(output);
}
if (input_pointer != NULL)
{
input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
}
return false;
}
其中utf16_literal_to_utf8用来把UTF-16字面量转换为UTF-8具体实现和编码相关,可以百度相关资料。
解析数字
遍历字符串拷贝出与数字相关的字符(最多64个),然后通过strtod转化为浮点数,设置
数值。
/* Parse the input text to generate a number, and populate the result into item. */
static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
{
double number = 0;
unsigned char *after_end = NULL;
unsigned char number_c_string[64];
unsigned char decimal_point = get_decimal_point();// 小数点符号
size_t i = 0;
if ((input_buffer == NULL) || (input_buffer->content == NULL))
{
return false;
}
/* copy the number into a temporary buffer and replace '.' with the decimal point
* of the current locale (for strtod)
* This also takes care of '\0' not necessarily being available for marking the end of the input */
// 遍历字符串如果不是与数字相关的字符就结束循环
for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
{
switch (buffer_at_offset(input_buffer)[i])
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '+':
case '-':
case 'e':
case 'E':
number_c_string[i] = buffer_at_offset(input_buffer)[i];
break;
case '.':
number_c_string[i] = decimal_point;
break;
default:
goto loop_end;
}
}
loop_end:
number_c_string[i] = '\0';
// 字符串转浮点数
number = strtod((const char*)number_c_string, (char**)&after_end);
if (number_c_string == after_end)
{
return false; /* parse_error */
}
// 设置数值
item->valuedouble = number;
/* use saturation in case of overflow */
if (number >= INT_MAX)
{
item->valueint = INT_MAX;
}
else if (number <= (double)INT_MIN)
{
item->valueint = INT_MIN;
}
else
{
item->valueint = (int)number;
}
item->type = cJSON_Number;
input_buffer->offset += (size_t)(after_end - number_c_string);
return true;
}
其中get_decimal_point用来获得本地小数点符号:
/* get the decimal point character of the current locale */
static unsigned char get_decimal_point(void)
{
#ifdef ENABLE_LOCALES
struct lconv *lconv = localeconv();
return (unsigned char)lconv->decimal_point[0];
#else
return '.';
#endif
}
解析数组
先判断是否为数组,然后判断是否为空数组,申请空间,解析下一个节点,如果有",",继续解析,直道解析到数组尾部。其中嵌套解析深度最多为CJSON_NESTING_LIMIT。
/* Build an array from input text. */
static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
{
cJSON *head = NULL; /* head of the linked list */
cJSON *current_item = NULL;
// 检查嵌套深度
if (input_buffer->depth >= CJSON_NESTING_LIMIT)
{
return false; /* to deeply nested */
}
input_buffer->depth++;
// 数组以[开头
if (buffer_at_offset(input_buffer)[0] != '[')
{
/* not an array */
goto fail;
}
// 空数组直接以]结束
input_buffer->offset++;
buffer_skip_whitespace(input_buffer);
if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
{
/* empty array */
goto success;
}
/* check if we skipped to the end of the buffer */
if (cannot_access_at_index(input_buffer, 0))
{
input_buffer->offset--;
goto fail;
}
/* step back to character in front of the first element */
input_buffer->offset--;
/* loop through the comma separated array elements */
// 循环解析直到没有,
do
{
/* allocate next item */
// 申请空间
cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
if (new_item == NULL)
{
goto fail; /* allocation failure */
}
/* attach next item to list */
// 组成链表
if (head == NULL)
{
/* start the linked list */
current_item = head = new_item;
}
else
{
/* add to the end and advance */
current_item->next = new_item;
new_item->prev = current_item;
current_item = new_item;
}
/* parse next value */
// 解析下一个节点
input_buffer->offset++;
buffer_skip_whitespace(input_buffer);
if (!parse_value(current_item, input_buffer))
{
goto fail; /* failed to parse value */
}
buffer_skip_whitespace(input_buffer);
} while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
// 检查是否为]结尾
if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
{
goto fail; /* expected end of array */
}
success:
input_buffer->depth--;
if (head != NULL)
{
head->prev = current_item;
}
item->type = cJSON_Array;
item->child = head;
input_buffer->offset++;
return true;
// 发生异常
fail:
if (head != NULL)
{
cJSON_Delete(head);
}
return false;
}
解析对象
先判断是否为对象,然后判断是否为空对象,申请空间,解析下一个节点(通过":“分割键和值对),如果有”,",继续解析,直道解析到对象尾部。
和解析数组类似,就不贴代码了。
json序列化
cJSON_Print是格式化输出,
cJSON_PrintUnformatted是压缩输出,
cJSON_PrintBuffered是格式化\压缩输出,预先估计了输出字符串的大小(可扩容),
cJSON_PrintPreallocated是格式化\压缩输出,预先分配了缓冲区(不可扩容)。
/* Render a cJSON item/entity/structure to text. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
{
return (char*)print(item, true, &global_hooks);
}
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
{
return (char*)print(item, false, &global_hooks);
}
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
{
printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
if (prebuffer < 0)
{
return NULL;
}
p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
if (!p.buffer)
{
return NULL;
}
p.length = (size_t)prebuffer;
p.offset = 0;
p.noalloc = false;
p.format = fmt;
p.hooks = global_hooks;
if (!print_value(item, &p))
{
global_hooks.deallocate(p.buffer);
return NULL;
}
return (char*)p.buffer;
}
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
{
printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
if ((length < 0) || (buffer == NULL))
{
return false;
}
p.buffer = (unsigned char*)buffer;
p.length = (size_t)length;
p.offset = 0;
p.noalloc = true;
p.format = format;
p.hooks = global_hooks;
return print_value(item, &p);
}
其中看到了printbuffer,是一个序列化工作区结构体:
typedef struct
{
unsigned char *buffer;// 缓冲区
size_t length;// 缓冲区大小
size_t offset;// 缓冲区使用偏移
size_t depth; /* current nesting depth (for formatted printing) */
cJSON_bool noalloc;// 是否可以扩容 false可扩容 true不可扩容
cJSON_bool format; /* is this print a formatted print */
internal_hooks hooks;// 内存管理函数
} printbuffer;
print序列化先预估256大小的缓冲区,可扩容,序列化结束后删除缓冲区多余的部分
static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
{
static const size_t default_buffer_size = 256;
printbuffer buffer[1];
unsigned char *printed = NULL;
memset(buffer, 0, sizeof(buffer));
/* create buffer */
// 创建buffer默认256大小
buffer->buffer = (unsigned char*)hooks->allocate(default_buffer_size);
buffer->length = default_buffer_size;
buffer->format = format;
buffer->hooks = *hooks;
if (buffer->buffer == NULL)
{
goto fail;
}
/* print the value */
// 序列化
if (!print_value(item, buffer))
{
goto fail;
}
update_offset(buffer);
// 删除缓冲区多余的部分
/* check if reallocate is available */
if (hooks->reallocate != NULL)
{
printed = (unsigned char*)hooks->reallocate(buffer->buffer, buffer->offset + 1);
if (printed == NULL)
{
goto fail;
}
buffer->buffer = NULL;
}
else /* otherwise copy the JSON over to a new buffer */
{
printed = (unsigned char*)hooks->allocate(buffer->offset + 1);
if (printed == NULL)
{
goto fail;
}
memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
printed[buffer->offset] = '\0'; /* just to be sure */
/* free the buffer */
hooks->deallocate(buffer->buffer);
}
return printed;
// 发生异常
fail:
if (buffer->buffer != NULL)
{
hooks->deallocate(buffer->buffer);
}
if (printed != NULL)
{
hooks->deallocate(printed);
}
return NULL;
}
#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
/* calculate the new length of the string in a printbuffer and update the offset */
// 计算printbuffer中字符串的新长度并更新偏移量
static void update_offset(printbuffer * const buffer)
{
const unsigned char *buffer_pointer = NULL;
if ((buffer == NULL) || (buffer->buffer == NULL))
{
return;
}
buffer_pointer = buffer->buffer + buffer->offset;
buffer->offset += strlen((const char*)buffer_pointer);
}
print_value根据节点类型不同进入对应的处理函数(和解析相反):
/* Render a value to text. */
// 序列化
static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
{
unsigned char *output = NULL;
if ((item == NULL) || (output_buffer == NULL))
{
return false;
}
switch ((item->type) & 0xFF)
{
case cJSON_NULL:
output = ensure(output_buffer, 5);
if (output == NULL)
{
return false;
}
strcpy((char*)output, "null");
return true;
case cJSON_False:
output = ensure(output_buffer, 6);
if (output == NULL)
{
return false;
}
strcpy((char*)output, "false");
return true;
case cJSON_True:
output = ensure(output_buffer, 5);
if (output == NULL)
{
return false;
}
strcpy((char*)output, "true");
return true;
case cJSON_Number:
return print_number(item, output_buffer);
// 原始字符串拷贝到\0结束
case cJSON_Raw:
{
size_t raw_length = 0;
if (item->valuestring == NULL)
{
return false;
}
raw_length = strlen(item->valuestring) + sizeof("");
output = ensure(output_buffer, raw_length);
if (output == NULL)
{
return false;
}
memcpy(output, item->valuestring, raw_length);
return true;
}
case cJSON_String:
return print_string(item, output_buffer);
case cJSON_Array:
return print_array(item, output_buffer);
case cJSON_Object:
return print_object(item, output_buffer);
default:
return false;
}
}
其中ensure用来确保printbuffer剩余空间是否能容纳接下来的字符串,如果不能就扩容。
/* realloc printbuffer if necessary to have at least "needed" bytes more */
// 检查剩余空间是否足够 不够就扩容
static unsigned char* ensure(printbuffer * const p, size_t needed)
{
unsigned char *newbuffer = NULL;
size_t newsize = 0;
if ((p == NULL) || (p->buffer == NULL))
{
return NULL;
}
if ((p->length > 0) && (p->offset >= p->length))
{
/* make sure that offset is valid */
return NULL;
}
// 整数上溢
if (needed > INT_MAX)
{
/* sizes bigger than INT_MAX are currently not supported */
return NULL;
}
// 检查是否满足需求
needed += p->offset + 1;
if (needed <= p->length)
{
return p->buffer + p->offset;
}
// 不能扩容就结束了
if (p->noalloc)
{
return NULL;
}
/* calculate new buffer size */
// 扩容为需要的两倍大小 如果上溢出则扩容为INT_MAX
if (needed > (INT_MAX / 2))
{
/* overflow of int, use INT_MAX if possible */
if (needed <= INT_MAX)
{
newsize = INT_MAX;
}
else
{
return NULL;
}
}
else
{
newsize = needed * 2;
}
if (p->hooks.reallocate != NULL)
{
/* reallocate with realloc if available */
// 可以使用系统的扩容函数 帮我们做了拷贝工作
newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
if (newbuffer == NULL)
{
p->hooks.deallocate(p->buffer);
p->length = 0;
p->buffer = NULL;
return NULL;
}
}
else
{
/* otherwise reallocate manually */
// 自己申请空间 需要自己拷贝用来的内容
newbuffer = (unsigned char*)p->hooks.allocate(newsize);
if (!newbuffer)
{
p->hooks.deallocate(p->buffer);
p->length = 0;
p->buffer = NULL;
return NULL;
}
memcpy(newbuffer, p->buffer, p->offset + 1);
p->hooks.deallocate(p->buffer);
}
p->length = newsize;
p->buffer = newbuffer;
return newbuffer + p->offset;
}
其中扩容大小为需要的两倍大小,如果上溢出则扩容为INT_MAX。
以前的版本是使用函数pow2gt计算的大于等于x的2的n次方:
static int pow2gt(int x)
{
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x + 1;
}
三种使用方法
// __declspec(dllexport) 导出dll
// __declspec(dllimport) 导入dll
#if defined(CJSON_HIDE_SYMBOLS)
#define CJSON_PUBLIC(type) type CJSON_STDCALL
#elif defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
#elif defined(CJSON_IMPORT_SYMBOLS)
#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
#endif
不导出
CJSON_HIDE_SYMBOLS 不导出dll 如果复制粘贴头文件和源文件到其他项目使用:
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_HIDE_SYMBOLS
#endif
导出为dll
CJSON_EXPORT_SYMBOLS 导出dll 编译dll使用:
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_EXPORT_SYMBOLS
#endif
导入dll
CJSON_IMPORT_SYMBOLS 导入dll 复制粘贴头文件以及dll、lib到其他项目使用:
如果下载我的测试代码运行的时候需要改一下路径:
#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
#define CJSON_IMPORT_SYMBOLS
#endif
单元测试
项目还有功能的测试:
源码链接
github下载
百度云链接:https://pan.baidu.com/s/1qasHBCUbh8GK4KFdxeUuYg
提取码:ui3n