Cjson学习

1.c语言是如何解析json的

在python里面,我们有json模块可以直接解析json。但是对其中原理,却不是很明白。这个cjson项目,就是用c语言写的解析json的。而且代码量不是很多,很适合用来学习如何解析json数据

2.开始调试

下载的文件有三个文件,分别是cjson.h和cjson.c,test.c。我们先来cjson.h看看

/* cJSON Types: */
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6

#define cJSON_IsReference 256
#define cJSON_StringIsConst 512

这里定义了,json里面的几种类型。分别是bool类型,数字类型,字符串类型,数组类型以及对象类型。

/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev;/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child;/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */

int type;/* The type of the item, as above. */

char *valuestring;/* The item's string, if type==cJSON_String */
int valueint;/* The item's number, if type==cJSON_Number */
double valuedouble;/* The item's number, if type==cJSON_Number */

char *string;/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;

下面定义了一个cJSON的结构体。这个结构体抽象的是json简单的键值对。一个json是由多个键值对组成的。后面通过链表将这些结构体链接起来。

开始调试看逻辑

这个项目给了一个测试文件test.c。我们想了解一下这个运行逻辑,就先把项目跑起来。然后自己打断点调试

void doit(char *text)
{
char *out;cJSON *json;

json=cJSON_Parse(text);
if (!json) {printf("Error before: [%s]\n",cJSON_GetErrorPtr());}
else
{
out=cJSON_Print(json);
cJSON_Delete(json);
printf("%s\n",out);
free(out);
}
}

首先我们进入这个函数,这个函数里面有个cJSON_Parse(text);函数,看到这个函数名字就知道它是用来解析json字符串的。

/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}

这个*cJSON_Print()函数的定义就是在这里,在里面又调用了cJSON_ParseWithOpts。

cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
//这个地方调用cJSON_New_Item();申请了一个cjson的结构体。就是抽象出来的键值对
cJSON *c=cJSON_New_Item();
ep=0;
//如果没有申请失败,就返回0
if (!c) return 0;       /* memory fail */
//这个skip就是获取下一个字符,将前面生成的结构体和value第二个字符传入parse_value进行解析
end=parse_value(c,skip(value));
if (!end){cJSON_Delete(c);return 0;}/* parse failure. ep is set. */

/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}}
if (return_parse_end) *return_parse_end=end;
return c;
}

这个里面的skip函数,就是去掉一些不必要的字符。

static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}

然后我们进入*parse_value这个函数看看

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. */
}

这个地方进行了一个判断,根据传进来的value值进行判断

我们第一次进入的话,肯定是字符‘{’这样我们就进入到解析对象的函数

parse_object

static const char *parse_object(cJSON *item,const char *value)
{
cJSON *child;//生成一个child结构体
//如果传进来的不不等于{,直接退出
if (*value!='{'){ep=value;return 0;}/* not an object! */

item->type=cJSON_Object;//设置类型为cJSON_Object
value=skip(value+1);//这里将指针后移一位,传入skip去掉一些特殊字符
if (*value=='}') return value+1;/* empty array. */

item->child=child=cJSON_New_Item();//这个地方也是申请一个新的cjson的结构体
if (!item->child) return 0;//申请失败,直接返回
value=skip(parse_string(child,skip(value)));//这里直接将value通过parse_string函数,将获取的键值放到child里面
if (!value) return 0;//如果前面的函数执行失败,!value为false,则直接返回0
child->string=child->valuestring;child->valuestring=0;//这个地方将获取的键的值放到child里面
if (*value!=':') {ep=value;return 0;}/* fail! */
//这个地方继续去解析对应的值,放到child里面
value=skip(parse_value(child,skip(value+1)));/* skip any spacing, get the value. */
if (!value) return 0;
//value解析到这里,应该就是,这个字符。接下来就是一个循环,重复解析了
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;
}
//当这个value对应的值为}时候,说明当前对象解析结束,应该结束当前对象解析,将指针指向下一个字符,然后返回
if (*value=='}') return value+1;/* end of array */
ep=value;return 0;/* malformed. */
}

通过我们这几步操作其实就已经生成一个链表。接下来就是输出这个链表了。输出函数的话,是在*print_object这个函数里面

static char *print_object(cJSON *item,int depth,int fmt,printbuffer *p)
{
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
size_t tmplen=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries)
{
if (p) out=ensure(p,fmt?depth+4:3);
elseout=(char*)cJSON_malloc(fmt?depth+4:3);
if (!out)return 0;
ptr=out;*ptr++='{';
if (fmt) {*ptr++='\n';for (i=0;i<depth-1;i++) *ptr++='\t';}
*ptr++='}';*ptr++=0;
return out;
}
if (p)
{
/* Compose the output: */
i=p->offset;
len=fmt?2:1;ptr=ensure(p,len+1);if (!ptr) return 0;
*ptr++='{';if (fmt) *ptr++='\n';*ptr=0;p->offset+=len;
child=item->child;depth++;
while (child)
{
if (fmt)
{
ptr=ensure(p,depth);if (!ptr) return 0;
for (j=0;j<depth;j++) *ptr++='\t';
p->offset+=depth;
}
print_string_ptr(child->string,p);
p->offset=update(p);

len=fmt?2:1;
ptr=ensure(p,len);if (!ptr) return 0;
*ptr++=':';if (fmt) *ptr++='\t';
p->offset+=len;

print_value(child,depth,fmt,p);
p->offset=update(p);

len=(fmt?1:0)+(child->next?1:0);
ptr=ensure(p,len+1); if (!ptr) return 0;
if (child->next) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
p->offset+=len;
child=child->next;
}
ptr=ensure(p,fmt?(depth+1):2); if (!ptr) return 0;
if (fmt)for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr=0;
out=(p->buffer)+i;
}
else
{
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {cJSON_free(entries);return 0;}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);

/* Collect all the results into our arrays: */
child=item->child;depth++;if (fmt) len+=depth;
while (child)
{
names[i]=str=print_string_ptr(child->string,0);
entries[i++]=ret=print_value(child,depth,fmt,0);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
child=child->next;
}

/* Try to allocate the output string */
if (!fail)out=(char*)cJSON_malloc(len);
if (!out) fail=1;

/* Handle failure */
if (fail)
{
for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
cJSON_free(names);cJSON_free(entries);
return 0;
}

/* Compose the output: */
*out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
for (i=0;i<numentries;i++)
{
if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
tmplen=strlen(names[i]);memcpy(ptr,names[i],tmplen);ptr+=tmplen;
*ptr++=':';if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';*ptr=0;
cJSON_free(names[i]);cJSON_free(entries[i]);
}

cJSON_free(names);cJSON_free(entries);
if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
*ptr++='}';*ptr++=0;
}
return out;
}

这个函数的话,就是遍历这个传进来的链表,根据不同类型,进入不同的输出函数进行输出。输出完成后,还要对空间进行释放。

总结

这个csjon项目的代码宗地来说比较少·,也有注释。理解起来也比较容易。如果对编译原理感兴趣,想用c语言自制一门编程语言,但是直接看书又不大理解,可以先学一下这个项目试试手。当然,我的这个写的不是很详情,像如何解析数组,具体如何输出的等,当然我上面也可能有错误。大家注意甄别。

下载项目的话,自己百度一下就行。这个项目我下载了,上传我的githup了。地址是https://github.com/ddouworld/study_cjson.git

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值