小白的第一个C语言博客项目-cJSON使用详解

小白的第一个C语言博客项目-cJSON使用详解

  • 说明
  • 1.JSON数据格式及cJSON项目的简单介绍
  • 2. cJSON项目介绍
    • 2.1.字符串类型的数据转化为JSON数据格式
      • 总结
    • 2.2.对json数据格式进行解析,获得数据
      • 总结

说明

本套教程包括两部分,cJSON的使用介绍以及从JSON的源码解析,这里是其第一部分内容,第二部分在这里
这篇文章中的例子参考了这篇文章,并在一定程度上进行了修改。

1.JSON数据格式及cJSON项目的简单介绍

JSON其实就是一个独立于任何编程语言的独立的轻量的数据交换的东西,方便于人的阅读和机器的解析。里面的内容还是键值对的形式存在的,可以对其进行增删改查。
JSON 格式常用于网络传输, 相对于 xml 格式, 存储需要的内存更小。

JSON 语法是 JavaScript 对象表示法语法的子集。 数据在键/值对中; 数据由逗号分隔; 花括号保存对象, 也称一个文档对象; 方括号保存数组, 每个数组成员用逗号隔开, 并且每个数组成员可以是文档对象或者数组或者键值对 。

JSON 的三种语法:
1.键/值对 key:value, 用半角冒号分割。 比如 “name”:“Faye”
2.文档对象 JSON ,对象写在花括号中, 可以包含多个键/值对。 比如{ “name”:“Faye” ,“address”:“北京” }。
3.数组 JSON ,数组在方括号中书写: 数组成员可以是对象, 值, 也可以是数组(只要有意义)。 {“love”: [“乒乓球”,“高尔夫”,“斯诺克”,“羽毛球”,“LOL”,“撩妹”]}

cJSON 开源项目位置:项目所在位置
json在线解析器:项目所在位置 , 可以用来检查输入的json数据是否符合语法:

输入下面的句子
小白的第一个C语言博客项目-cJSON使用详解_第1张图片

解析结果
小白的第一个C语言博客项目-cJSON使用详解_第2张图片

删除最后面的大括号,制造语法错误
小白的第一个C语言博客项目-cJSON使用详解_第3张图片

2. cJSON项目介绍

cJSON项目包括三个文件。test.c(下图的main.c)、cJSON.h、cJSON.c小白的第一个C语言博客项目-cJSON使用详解_第4张图片
接下来将通过几个简单的例子,对cJSON的API使用进行介绍

2.1.字符串类型的数据转化为JSON数据格式

创建json格式的数据串,这个串可以是对象(object,大括号标识{ },成员之间由逗号隔开),可以是数组(array,中括号标识[ ],成员之间由逗号隔开)。

<例1>创建对象,向对象里面添加字符串和数值,这里有两级object

int main (int argc, const char * argv[]) {
	/* Our "Video" datatype: */
	root=cJSON_CreateObject();// 类似于创建了一个大括号
	//向root添加结点,"name"为键,"Jack (\"Bee\") Nimble")为对应的值
	cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
	//向root添加结点,"format"为键,fmt为对应的值,这里是一个object的结构
	cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
	//向fmt添加结点,"type"为键,"rect"为对应的值
	cJSON_AddStringToObject(fmt,"type",		"rect");
	//添加结点,"width"为键,1920为对应的值
	cJSON_AddNumberToObject(fmt,"width",		1920);
	//向fmt添加结点,"height"为键,1080为对应的值
	cJSON_AddNumberToObject(fmt,"height",		1080);
	//向fmt添加结点,"interlace"为键,函数会自动创建键值为"false"
	cJSON_AddFalseToObject (fmt,"interlace");
	//向fmt添加结点,"frame rate"为键,24为对应的键
	cJSON_AddNumberToObject(fmt,"frame rate",	24);
	out=cJSON_Print(root);	// 将cJSON结构的内容,组织成字符串,out指向这部分内存
	cJSON_Delete(root);	//释放由malloc申请的关于结构方面的动态内存
	printf("%s\n",out);	//打印
	free(out); //释放malloc申请的字符串的动态内存
	return 0;
}

说明
1.上面关于给出了代码的注释,可以参照理解程序
2.cJSON_CreateObject函数创建object,之后便可向该根数据项中添加字符串、数值型、array以及object等内容,返回 cJSON的指针,需要注意的是需要调用cJSON_Delete(root); 对由malloc申请的动态内存进行回收。
3.cJSON_Delete只释放了cJSON结构以及cJSON结构成员动态申请的内存,各个结构组合在一起的字符串的内存由变量out指向,依旧需要调用free释放内存。

输出的内容如下
小白的第一个C语言博客项目-cJSON使用详解_第5张图片

这段代码创建的结构链如下
暂时不懂为什么是这样没有什么关系,阅读第二篇cJSON源码详解后,相信你会彻底明白这些。
小白的第一个C语言博客项目-cJSON使用详解_第6张图片

<例2>创建数组,向数组里面添加字符串和数值

int main (int argc, const char * argv[]) {
    cJSON *root;
    root = cJSON_CreateArray();  //创建一个array
    cJSON_AddItemToArray(root, cJSON_CreateString("Yuan"));
    cJSON_AddItemToArray(root, cJSON_CreateNumber(1314));

    // 两种打印方式,这一种打印方式会进行格式化输出\n,\t之类的
    char *s1 = cJSON_Print(root);
    // 直接输出一句字符串,不加入格式
    char *s2 = cJSON_PrintUnformatted(root);

    // 判断是否创建成功,成功的话释放内存
    if(root)
        cJSON_Delete(root);
    // 判断是否转化为字符串成功,成功的话打印并释放动态内存
    if(s1)
    {
       printf(" 带有格式的输出结果:\n%s \n",s1);
       free(s1);
    }
    // 判断是否转化为字符串成功,成功的话打印并释放动态内存
    if(s2)
    {
       printf(" 不带格式的输出结果:\n%s \n",s2);
       free(s2);
    }
    return 0;

输出结果
从这里可以看出,带格式的打印两个单元之间,明显比不带格式的打印多了空白字符,这里区分不大,下一个例子会比较明显。
小白的第一个C语言博客项目-cJSON使用详解_第7张图片

<例3>对象里面包括一个数组,数组里面包括对象,对象里面再添加一个字符串和一个数字

int main (int argc, const char * argv[]) {
	cJSON *root, *array,*subObject;
    root = cJSON_CreateObject();  // 创建一个object,一个大括号{}
    // 在root结点下面添加新节点,结点的键为"information",值为一个array的数组[ ]
    cJSON_AddItemToObject(root,"information", array = cJSON_CreateArray()); 
    // 在array结点下添加新节点,新节点为一个object对象
    cJSON_AddItemToArray(array, subObject = cJSON_CreateObject());
    // 在subObject下面添加新节点,键的内容为"lastname",值为"Yuan"
    cJSON_AddStringToObject(subObject,"lastname","Yuan");
    // 在subObject下面添加新节点,键的内容为"lastname",值为100
    cJSON_AddNumberToObject(subObject,"ID",100);

    char *s1 = cJSON_Print(root); // 带有格式的打印,换行符、制表符、空格等
    char *s2 = cJSON_PrintUnformatted(root); // 不带格式的打印,效果可以看后面的输出结果
    if(root)
        cJSON_Delete(root);
    // 判断是否转化为字符串成功,成功的话打印并释放动态内存
    if(s1)
    {
       printf(" 带有格式的输出结果:\n%s \n",s1);
       free(s1);
    }
    // 判断是否转化为字符串成功,成功的话打印并释放动态内存
    if(s2)
    {
       printf(" 不带格式的输出结果:\n%s \n",s2);
       free(s2);
    }

    return 0;

输出结果

从这里可以明显的看出带格式不带格式打印字符的区别:
这段代码的内容,可以结合注释以及例1给出链进行理解

小白的第一个C语言博客项目-cJSON使用详解_第8张图片

总结

创建json结构的过程就像搭积木一样,需要注意的是父子之间的关系,同时注意区分array结点和object节点和普通数据结点之间的区别。

2.2.对json数据格式进行解析,获得数据

json数据的解析主要是通过cJSON_Parse函数实现的,是前面例子的逆过程,指定键,从对应的cJSON结构体中提取对应的值

text = "{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\":       \"rect\", \n\"width\":      1920, \n\"height\":     1080, \n\"interlace\":  false,\"frame rate\": 24\n}\n}"
json=cJSON_Parse(text);//通过此函数将字符串存储到cJSON的结构块中,并且结构块之间相连

<例1>字符串之间不存在父节点所有的成员都属于同一级别

int main (int argc, const char * argv[]) {
	cJSON *json,*jsonName,*jsonPasswd,*jsonNum;
    char* out="{\"name\":\"fengxin\",\"passwd\":\"123\",\"num\":1}";
    json = cJSON_Parse(out); //解析成json形式
     if(!json)
     	return 0;
    jsonName = cJSON_GetObjectItem( json , "name" );  //获取指定的键对应的内容
    jsonPasswd = cJSON_GetObjectItem( json , "passwd" );//获取指定的键对应的内容
    jsonNum = cJSON_GetObjectItem( json , "num" );//获取指定的键对应的内容
    printf("%s","输出结果如下:\n");
    printf("name:%s\npasswd:%s\nnum:%d\n",jsonName->valuestring,jsonName->valuestring,jsonNum->valueint);
    cJSON_Delete(json);  //释放内存
    return 0;

说明:
这里在打印值的时候,不同类型的键值对使用了结构体中不同的成员,比如打印数字使用:valueint;打印字符串使用:valuestring,这是建立在对cJSON结构了解的基础上,实际上需要对cJSON结构体成员中type变量判断,在进行打印,代码方面的细节看cJSON源码分析

结果如下
完成了对json数据的解析,可以通过指定键获取其对应的值:
小白的第一个C语言博客项目-cJSON使用详解_第9张图片

<例2>一个父object中包含两个子object,子object存储具体内容,即存在父层结构

int main (int argc, const char * argv[]) {
    char *s = "{\"list\":{\"name\":\"xiao hong\",\"age\":10},\"other\":{\"name\":\"hua hua\"}}";
    
    cJSON *root = cJSON_Parse(s); //将字符串转化为对应的结构
    if(!root) {  // 转化出错返回
        printf("get root faild !\n");
        return -1;
    }
    // 获取子object单元
    cJSON *js_list = cJSON_GetObjectItem(root, "list");
    if(!js_list) { //判断是否成功
        printf("no list!\n");
        return -1;
    }
    // 打印子object的类型,输出为6因为object的type被宏定义为6
    printf("第一个子object类型是: %d\n",js_list->type);
    // 从子object根据指定的键输出对应的内容 
    cJSON *name = cJSON_GetObjectItem(js_list, "name");
    if(!name) {
        printf("No name !\n");
        return -1;
    }
    // 输出为4,字符串的结构的type被定义为4
    printf("第一个子object的第一个成员的类型是: %d\n",name->type);
    printf("对应的值是 %s\n",name->valuestring);
    //从子object根据指定的键输出对应的内容
    cJSON *age = cJSON_GetObjectItem(js_list, "age");
    if(!age) {
        printf("no age!\n");
        return -1;
    }
    //输出为3,字符串的结构的type被定义为3
    printf("第一个子object的第二个成员的类型是: %d\n", age->type);
    printf("对应的值是 %d\n",age->valueint);

     
    //获取另一个子object
    cJSON *js_other = cJSON_GetObjectItem(root, "other");
    if(!js_other) {
        printf("no list!\n");
        return -1;
    }
    // 打印子object的类型,输出为6因为object的type被宏定义为6
    printf("第二个子object类型是: %d\n",js_other->type);
     
    cJSON *js_name = cJSON_GetObjectItem(js_other, "name");
    if(!js_name) {
        printf("No name !\n");
        return -1;
    }
     // 输出为4,字符串的结构的type被定义为4
	printf("第二个子object的第一个成员的类型是: %d\n",js_name->type);
    printf("对应的值是 %s\n",js_name->valuestring);   
    if(root) //释放内存
        cJSON_Delete(root);
    return 0;
}

结果如下
从这段代码可以看出实际上具有父节点的json结构只需要逐层提取就可以,就像剥洋葱一样,一层层的剥开
小白的第一个C语言博客项目-cJSON使用详解_第10张图片
<例3>提取数组中的内容

int main (int argc, const char * argv[]) {
	char *s = "{\"list\":[\"name1\",\"name2\"]}";
    cJSON *root = cJSON_Parse(s);
    if(!root) {
        printf("get root faild !\n");
        return -1;
    }
    cJSON *js_list = cJSON_GetObjectItem(root, "list");
    if(!js_list){
        printf("no list!\n");
        return -1;
    }
    //获取数组的大小
    int array_size = cJSON_GetArraySize(js_list);
    printf("数组的大小是  %d\n",array_size);
    int i = 0;
    cJSON *item;
    for(i=0; i< array_size; i++) {
        //获取指定位置的cJSON结构体
        item = cJSON_GetArrayItem(js_list, i);
        printf("结构的类型是 %d\n",item->type);
        printf("对应的值是 %s\n",item->valuestring);
    }
	// 释放内存
    if(root)
        cJSON_Delete(root);
    return 0;	
 }

说明
这里可以看出在数组array中获取元素和在object获取元素的区别,在array是通过其在数组中的位置来获取,而在object中则是根据键值匹配获取,实际上object对象的获取调用了array的方法,二者都是通过位置来获取对应的值
输出结果为

小白的第一个C语言博客项目-cJSON使用详解_第11张图片
<例4>对象中包含数组,数组中又包含对象,对象内包含数据


    char *s = "{\"list\":[{\"name\":\"xiao hong\",\"age\":10},{\"name\":\"hua hua\",\"age\":11}]}";
    cJSON *root = cJSON_Parse(s);
    if(!root) {
        printf("get root faild !\n");
        return -1;
    }
    //获取父节点"list"下的array
    cJSON *js_list = cJSON_GetObjectItem(root, "list");
    if(!js_list){
        printf("no list!\n");
        return -1;
    }
    // 计算数组的大小
    int array_size = cJSON_GetArraySize(js_list);
    printf("数组的大小是 %d\n",array_size);
    int i = 0;
    cJSON *item,*it, *js_name, *js_age;
    char *p  = NULL;
    for(i=0; i< array_size; i++) {
        item = cJSON_GetArrayItem(js_list, i);
        if(!item) {
            return -1;
            printf("%s\n","出错了!");
        }
        // 根据子object得到其对应的字符串
        p = cJSON_PrintUnformatted(item);
        // 对子object的字符串创建一条新的数据链
        it = cJSON_Parse(p);

        if(!it)
            continue ;

        js_name = cJSON_GetObjectItem(it, "name");
        printf("名字是 %s\n",js_name->valuestring);
        js_age = cJSON_GetObjectItem(it, "age");
        printf("年龄是 %d\n",js_age->valueint);

        free(p); // 释放运行过程中产生的动态内存,调用cJSON_PrintUnformatted产生
        cJSON_Delete(it); // // 释放运行过程中产生的动态内存,调用 cJSON_Parse(p)产生

    }

    if(root)
        cJSON_Delete(root);
        return 0;

说明
根据这个例子我们可以看到,对于嵌套了子object的结构,我们可以调用cJSON_PrintUnformatted获取子object对应的字符串,
输出结果
小白的第一个C语言博客项目-cJSON使用详解_第12张图片

总结

至此json解析的部分结束,想要更深层次的了解源码请看这里。

你可能感兴趣的:(项目学习,c++,c语言,数据结构)