cJSON使用问题记录

cJSON库使用笔记

JSON:: 轻量级的资料交换语言,该语言以易于让人阅读的文字为基础,用来传输由属性值或者序列性的值组成的数据对象。cJSON是一个轻量级的开源JSON解析器,能够实现使用C语言生成或解析json格式的对象,本文主要记录使用该解析器的过程中,遇到的一些问题。

New

通过了解cJSON库中用于定义cJSON对象的结构体与cJSON_New_Item函数:

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;

/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
	cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
	if (node) memset(node, 0, sizeof(cJSON));
	return node;
}

不难发现,本质上可以将cJSON对象理解为一个双向链表。在新建cJSON对象时,需要对其进行初始化操作,不然会造成存在野指针访问内存的问题。

//实例一个新的cJSON对象
cJSON *json_new = cJSON_CreateObject();

//解析一串符合json格式的字符串,并新建一个cJSON指针指向其解析结果
char *str = "{\"Title\":\"Hello World!\"}";
cJSON *json_parse = cJSON_Parse(str);

Delete

既然cJSON对象的空间是被动态分配出来的,在使用完该对象后自然也需要在内存中释放掉相关资源,cJSON中提供了cJSON_Delete(cJSON *obj);函数便于开发者释放cJSON对象的空间,但是需要注意,在释放空间时只需要释放最上层的父节点,例如以下cJSON对象被新建后:

cJSON *json_root = cJSON_CreateObject();
cJSON *json_array = cJSON_CreateArray();
cJSON *json_item1 = cJSON_CreateObject();
cJSON *json_item2 = cJSON_CreateObject();

cJSON_AddStringToObject(json_item1, "name" , "张三");
cJSON_AddNumberToObject(json_item1, "age" , 15);
cJSON_AddStringToObject(json_item2, "name" , "李四");
cJSON_AddNumberToObject(json_item2, "age" , 20);

cJSON_AddItemToArray(json_array, json_item1);
cJSON_AddItemToArray(json_array, json_item2);

cJSON_AddNumberToObject(json_root, "index" , 0);
cJSON_AddItemToObject(json_root, "list", json_array);

char *str_json = cJSON_Print(json_root);
printf("%s", str_json);
/*
{
    "index":1,
    "list":[
        {
            "name":"张三",
            "age":15
        },
        {
            "name":"李四",
            "age":20
        }
    ]
}
*/

cJSON_Delete(json_root);
free(str_json);

可以看到,在程序的起始部分,新建了四个cJSON对象,但在后续的操作中,json_item1与json_item2先后被添加至json数组json_list中,最后json_list被添加至json_root中,此时根据cJSON对象的结构体成员与cJSON_AddXxxToYyy函数的实现方法,能够看出通过访问json_root->child的子节点便能够遍历其他三个子节点,这时只需要对父节点调用cJSON_Delete()函数即可将子节点一同释放,若在Delete父节点后又对子节点进行了Delete操作,则会引发释放野指针的错误。

上述的对象需要Delete的问题其实有用过cJSON的程序猿应该都知道,接下来这个问题才是让我真正想记录这篇文章的原动力。。。

cJSON *json_root = cJSON_CreateObject();
cJSON_AddNumberToObject(json_root, "index" , 0);
printf(cJSON_Print(json_root));
cJSON_Delete(json_root);

上面这几行代码看着一点毛病也没有,增加、修改、输出、释放一气呵成,实际运行起来也的确成功输出了"{“index”:0}",那么问题点在哪呢?
将cJSON_Print“剥开”,一层一层往里面看,可以发现当需要输出数据类型为整型时,调用了如下函数:

/* Render the number nicely from the given item into a string. */
static char *print_number(cJSON *item, printbuffer *p)
{
	char *str = 0;
	double d = item->valuedouble;
	if (d == 0)
	{
		if (p)	str = ensure(p, 2);
		else	str = (char*)cJSON_malloc(2);	/* special case for 0. */
		if (str) strcpy(str, "0");
	}
	else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN)
	{
		if (p)	str = ensure(p, 21);
		else	str = (char*)cJSON_malloc(21);	/* 2^64+1 can be represented in 21 chars. */
		if (str)	sprintf(str, "%d", item->valueint);
	}
	else
	{
		if (p)	str = ensure(p, 64);
		else	str = (char*)cJSON_malloc(64);	/* This is a nice tradeoff. */
		if (str)
		{
			if (fabs(floor(d) - d) <= DBL_EPSILON && fabs(d)<1.0e60)sprintf(str, "%.0f", d);
			else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)			sprintf(str, "%e", d);
			else												sprintf(str, "%f", d);
		}
	}
	return str;
}

这也就直接导致了,每打印输出一次,内存中就会对应着产生一段被开辟而不被占用的内存(其实看返回的char*就能够判断出会有这个现象了,本质上还是自己太菜+写代码太不严谨),在电脑上可能用得好好的,但偶然有一次在STM32上用了cJSON,没跑一会直接给我报了个HardFault。。。。急得我把代码拷到VS上看了波内存占用情况,废了半天功夫才找到问题。

Edit

对Json对象中的某部分内容进行修改是一项极为正常的操作,当修改的数据为整型的时候,可能大概看了一遍cJSON的结构体定义,就决定直接使用json_obj->valueint = i的方法来赋值,而但凡看一遍了"cJSON.h",就能够发现在头文件的最底部有这么一个宏定义:

/* When assigning an integer value, it needs to be propagated to valuedouble too. */
#define cJSON_SetIntValue(object,val)			((object)?(object)->valueint=(object)->valuedouble=(val):(val))
#define cJSON_SetNumberValue(object,val)		((object)?(object)->valueint=(object)->valuedouble=(val):(val))

由注释可知,当Json对象中的整型数据被赋值时,需要同时设置下double值。虽然没有遇到过,但是看网上不少老哥踩到了这个坑。而在修改string类型的值时,可以使用cJSON_ReplaceItemInObject函数对对象进行操作。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值