1. JSON格式
JSON格式
C语言:json库的使用
cJSON库源代码获取:https://github.com/DaveGamble/cJSON.git
。
也可以从我的GitHub获取,包含了解析的打包的两个测试程序。
2. 解析
主要过程分为三步:
使用cJSON_Parse将字符串转换为cJSON结构。
使用cJSON_GetObjectItem获取指定键对应的值。
使用cJSON_Delete销毁创建的cJSON结构。
当涉及数组时,需要多使用两个函数:
int cJSON_GetArraySize(cJSON *array);
cJSON* cJSON_GetArrayItem(cJSON *array,int item);
3. 打包
注意事项:主要是内存回收。
cJSON打包功能使用-代码案例、特别注意事项
4. 源码分析
在test.c程序中,我们解析的是JSON如下:
char *Output2 = "{\"state\":1}";
对应到cJSON.c文件中的函数,重点看两个函数:
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value)
{
if (!value) return 0; /* Fail on null. */
if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; }
if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; }
if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; }
if (*value=='\"') { return parse_string(item,value); }
if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); }
if (*value=='[') { return parse_array(item,value); }
if (*value=='{') { return parse_object(item,value); }
ep=value;return 0; /* failure. */
}
在此函数中,可以看到根据第一个字符进入不同分支。我们主要看parse_object
函数:
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='{') {ep=value;return 0;} /* not an object! */
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1; /* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;child->valuestring=0;
if (*value!=':') {ep=value;return 0;} /* fail! */
value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}
尤其要注意链表的指向。在此函数之前,先创建了一个空的cJSON结构体,作为起始节点。其child指向的结构体才开始存储信息:
valueint = 1
string = state
5. 问题记录
-
date值是什么形式
-
打印输出后,换行符
-
小数点精度控制
程序中调用如下:
value = cJSON_CreateNumber(120.8);
输出变为120.800000。查看sJSON.c文件,cJSON_CreateNumber函数定义如下:
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
在函数内部增加
printtf("%f",num)
发现num已经变为120.800000
如果把形参改为float,型,打印后为120.800003
未解决,保留。
临时解决办法为固定小数点后两位,需要修改sJSON.c文件中的print_number
函数
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;
}
将最后一个sprintf修改为sprintf(str,"%.2lf",d);