cjson介绍-应用-实例-源码分析

2024-09-05 08:32

本文主要是介绍cjson介绍-应用-实例-源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.json简介

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。
想要进一步了解,可以参考  json 的官方介绍 ,它是英文版的,亦可参考 中文翻译版本 .。
同时,可参考xml语言,进行对比,此处不作详细说明,有兴趣的朋友可以参考网上文章。比如: https://www.cnblogs.com/yank/p/4028266.html

2.cjson介绍

            cjson 的代码只有 1000+ 行, 而且只是简单的几个函数的调用,使用方便,可参考https://sourceforge.net/projects/cjson/
详细介绍。 另:通过阅读sourceforge下载的源码资料中Readme,可掌握cjson使用方法及应用。

3.cjson应用实例

3.0.实例源码下载

本人使用codeblocks编译,运行ubuntu14.04,codeblocks13,实例借鉴他人代码,后面会列出链接,并在此表示感谢。
源码链接地址: http://download.csdn.net/download/u013359794/10246993

3.1.json标准格式

标准JSON的合法符号:{(左大括号)  }(右大括号)  "(双引号)  :(冒号)  ,(逗号)  [(左中括号)  ](右中括号)

JSON字符串:特殊字符可在字符前面加 \ 或使用 \u 加 4位16进制数来处理

[html]  view plain  copy
  1. {"name":"jobs"}  

JSON布尔:必须小写的true和false
[html]  view plain  copy
  1. {"bool":true}  
JSON空:必须小写的null
[html]  view plain  copy
  1. {"object":null}  
JSON数值:不能使用8/16进制
[html]  view plain  copy
  1. {"num":60}  
  2. {"num":-60}  
  3. {"num":6.6666}  
  4. {"num":1e+6}<!-- 1乘10的6次方,e不区分大小写 -->  
  5. {"num":1e-6}<!-- 1乘10的负6次方,e不区分大小写 -->  
JSON对象
[html]  view plain  copy
  1. {  
  2.     "starcraft": {  
  3.         "INC": "Blizzard",  
  4.         "price": 60  
  5.     }  
  6. }  
JSON数组
[html]  view plain  copy
  1. {  
  2.     "person": [  
  3.         "jobs",  
  4.         60  
  5.     ]  
  6. }  
JSON对象数组
[html]  view plain  copy
  1. {  
  2.     "array": [  
  3.         {  
  4.             "name": "jobs"  
  5.         },  
  6.         {  
  7.             "name": "bill",  
  8.             "age": 60  
  9.         },  
  10.         {  
  11.             "product": "war3",  
  12.             "type": "game",  
  13.             "popular": true,  
  14.             "price": 60  
  15.         }  
  16.     ]  
  17. }  

参考: http://www.json.org/json-zh.html

来源: http://blog.csdn.net/assholeu/article/details/43037373

3.2.使用cJSON创建JSON字符串

    在Linux下,使用C语言编程,开始JSON字符串的创建。我们还是一步步来,逐渐由简单到复制。  

1,下载源码

可以从如下网站来下载:https://sourceforge.net/projects/cjson/ 。

2,包含cJSON的源码

下载下来,解压后,从里面找到两个文件(cJSON.c、cJSON.h),复制到我们的工程里面。只需在函数中包含头文件(#include “cJSON.h”),然后和cJSON.c一起编译即可使用。 

3,创建一个键值对

         首先是一个简单的键值对字符串,要生成的目标如下:

{"firstName":"Brett"}

要进行创建,就是先确定键与值,然后转为cJSON格式。我们很容易就能明确键为firstName,值为Brett,可是,使用cJSON怎么创建呢? 

对于这个简单的例子,我们需要调用cJSON的五个接口函数就可以实现创建了。(有人不乐意了:都五个函数了,你还说“就可以了”----其实是一法通,百法通,学会了这个创建,其他的创建动作都是非常类似的。)

这五个函数的原型如下:

cJSON*cJSON_CreateObject ();

cJSON*cJSON_CreateString(const char *string);

voidcJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);

voidcJSON_Delete(cJSON *c);

char*cJSON_Print(cJSON *item); 

下面按创建过程来描述一次:

(1)       首先调用cJSON_ CreateObject ()函数,创建一个JSON对象,之后便可向这个对象中添加string或int等内容的数据项了。使用该函数会通过malloc()函数在内存中开辟一个空间,使用完成需要手动释放。

        cJSON*root=cJSON_CreateObject();

(2)       调用cJSON_CreateString ()函数,由一个字符串生成一个cJSON的数据项。

        cJSON*item=cJSON_CreateString("Brett");

(3)       将上一步生成的数据项与其键值("firstName")一起添加到root对象中。

        cJSON_AddItemToObject(root,"firstName",item);

其实到这一步,我们在内存中的cJSON对象就已经构建完成了,后面是展示结果了。

(4)       将cJSON对象的内容解析为字符串,并展示出来。

        out=cJSON_Print(root);

        printf("out:%s\n",out);

(5)       通过cJSON_Delete(),释放cJSON_CreateObject ()分配出来的内存空间。

        cJSON_Delete(root);

(6)       释放cJSON_Print ()分配出来的内存空间。

        free(out); 

         这样就完成了一次cJSON接口调用,实现了字符串的创建工作。

4,转换一个结构体 

接下来,我们来个复杂一点的,将一个结构体转换为JSON字符串,结构体定义如下:

typedefstruct

{

         char firstName[32];

         char lastName[32];

         char email[64];

         int age;

         float height;

} PERSON;

看起来比一个键值对复杂多了,我们又需要学习新的接口函数了吗?

是的,由于出现了数字,我们需要学习一个新函数:

cJSON *cJSON_CreateNumber(double num);

当然,创建的步骤要更复杂一些,下面我仍然是按创建过程来描述一次: 

(1)还是先调用cJSON_ CreateObject ()函数,创建一个JSON对象root,做为根(咱们可以把JSON串看成是一颗树)。使用该函数会通过malloc()函数在内存中开辟一个空间,使用完成需要手动释放。

        cJSON*root=cJSON_CreateObject();

(2)继续调用cJSON_ CreateObject ()函数,创建一个JSON对象obj_person,做为挂载结构体内容的对象。挂载内容之后,这个对象是要挂载到根上的。

        cJSON*obj_person=cJSON_CreateObject();

(3)根据数据生成cJSON格式的数据项,调用cJSON_AddItemToObject()函数挂载到obj_person对象上。这个过程,要多次重复,直到将所有数据都添加上。此时要注意,不同的成员,生成的方法是不一样的。

        cJSON*item=cJSON_CreateString(person->firstName);

        cJSON_AddItemToObject(obj_person,"firstName",item);

        item=cJSON_CreateString(person->lastName);

        cJSON_AddItemToObject(obj_person,"lastName",item);

        item=cJSON_CreateString(person->email);

        cJSON_AddItemToObject(obj_person,"email",item);

        item=cJSON_CreateNumber(person->age);

        cJSON_AddItemToObject(obj_person,"age",item);

        item=cJSON_CreateNumber(person->height);

        cJSON_AddItemToObject(obj_person,"height",item);

(4)将obj_person对象挂载到根上。

        cJSON_AddItemToObject(root,"person",obj_person);

到这一步,我们在内存中的cJSON对象就已经构建完成了,后面就是展示结果。

(5)将cJSON对象的内容解析为字符串,并展示出来。

        out=cJSON_Print(root);

        printf("out:%s\n",out);

(6)通过cJSON_Delete(),释放cJSON_CreateObject ()分配出来的内存空间。这里说明一下,我们前面调用了2次cJSON_CreateObject (),最后只需要针对root调用一次释放即可,因为第二次创建的对象也是挂接在root上的。

        cJSON_Delete(root);

(7)释放cJSON_Print ()分配出来的内存空间。

        free(out); 

         至此,我们就使用cJSON接口完成了由结构体生成JSON字符串的工作。 

5,创建结构体数组的JSON串   

         最后,我们来个更复杂一些的,来转换一个数组,并且数组的成员是结构体!我们要生成的目标如下:

{

"people":[

{"firstName":"z","lastName":"Jason","email":"bbbb@126.com","height":1.67},

{"lastName":"jadena","email":"jadena@126.com","age":8,"height":1.17},

{"email":"cccc@126.com","firstName":"z","lastName":"Juliet","age":36,"height":1.55}

]

此时,我们又需要学习新的接口了,一个是创建数组,一个是取数组成员,函数原型如下:

cJSON*cJSON_CreateArray(void);

void   cJSON_AddItemToArray(cJSON *array, cJSON*item); 

由于前面已经实现了单个结构体的转换,这里我们重点关注下数组的相关调用。 

(1)还是先调用cJSON_ CreateObject ()函数,创建一个JSON对象root,做为根。

(2)调用cJSON_CreateArray ()函数,创建一个JSON数组对象,准备挂载多个结构体对象。挂载内容之后,这个数组对象是要挂载到根上的。

        cJSON*array_person=cJSON_CreateArray();

(3)生成一个结构体对象,并相应添加数据,然后调用cJSON_AddItemToArray()函数挂载到数组对象上。这个过程,要多次重复,直到将所有结构体对象都添加上。

        cJSON_AddItemToArray(array_person,obj_person);

(4)将数组对象挂载到根上。

        cJSON_AddItemToObject(root,"people",array_person);

到这一步,我们在内存中的cJSON对象就已经构建完成了。

(5)将cJSON对象的内容解析为字符串,并展示出来。

(6)通过cJSON_Delete(),释放cJSON_CreateObject ()分配出来的内存空间。

(7)释放cJSON_Print ()分配出来的内存空间。

         这样,我们就使用cJSON接口完成了将结构体数组转换成JSON字符串的工作。

详细代码见后文附带例程。         

说明:

本文所附带例程,实现了结构体数组生成JSON字符串,只是一个学习之作,对于初学cJSON的同学,可以有些借鉴参考的作用。 

附带例程: 

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <sys/types.h>  
  4. #include <stdlib.h>  
  5. #include <unistd.h>  
  6.   
  7. #include "cJSON.h"  
  8.   
  9. typedef struct  
  10. {  
  11.     int id;  
  12.     char firstName[32];  
  13.     char lastName[32];  
  14.     char email[64];  
  15.     int age;  
  16.     float height;  
  17. }people;  
  18.   
  19. void dofile(char *filename);/* Read a file, parse, render back, etc. */  
  20.   
  21. int main(int argc, char **argv)  
  22. {  
  23.   
  24.     dofile("json_str4.txt");  
  25.   
  26.     return 0;  
  27. }  
  28.   
  29.   
  30. //create a key-value pair  
  31. int str_to_cJSON(char *json_string, char *str_val)  
  32. {  
  33.     char * out=NULL;  
  34.     cJSON *root=cJSON_CreateObject();  
  35.     if (!root)  
  36.     {  
  37.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  38.         return -1;  
  39.     }  
  40.     else  
  41.     {  
  42.         cJSON *item=cJSON_CreateString("Brett");  
  43.         cJSON_AddItemToObject(root,"firstName",item);  
  44.   
  45.         out=cJSON_Print(root);  
  46.         printf("out2:%s\n",out);  
  47.   
  48.         cJSON_Delete(root);  
  49.         if(out!=NULL)  
  50.         {  
  51.             free(out);  
  52.         }  
  53.     }  
  54.     return 0;  
  55. }  
  56.   
  57. //create a object from struct  
  58. int struct_to_cJSON(char *json_string, people *person)  
  59. {  
  60.   
  61.     if((json_string==NULL) || (person==NULL))  
  62.     {  
  63.         printf("%s: input is invalid",__func__);  
  64.     }  
  65.   
  66.     char * out=NULL;  
  67.     cJSON *root=cJSON_CreateObject();  
  68.   
  69.     if (!root)  
  70.     {  
  71.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  72.         return -1;  
  73.     }  
  74.     else  
  75.     {  
  76.         cJSON *obj_person=cJSON_CreateObject();  
  77.   
  78.         cJSON *item=cJSON_CreateString(person->firstName);  
  79.         cJSON_AddItemToObject(obj_person,"firstName",item);  
  80.   
  81.         item=cJSON_CreateString(person->lastName);  
  82.         cJSON_AddItemToObject(obj_person,"lastName",item);  
  83.   
  84.         item=cJSON_CreateString(person->email);  
  85.         cJSON_AddItemToObject(obj_person,"email",item);  
  86.   
  87.         item=cJSON_CreateNumber(person->age);  
  88.         cJSON_AddItemToObject(obj_person,"age",item);  
  89.   
  90.         item=cJSON_CreateNumber(person->height);  
  91.         cJSON_AddItemToObject(obj_person,"height",item);  
  92.   
  93.         cJSON_AddItemToObject(root,"person",obj_person);  
  94.   
  95.         out=cJSON_Print(root);  
  96.         printf("out2:%s\n",out);  
  97.   
  98.         cJSON_Delete(root);  
  99.         if(out!=NULL)  
  100.         {  
  101.             memcpy(json_string,out,strlen(out));  
  102.             free(out);  
  103.         }  
  104.     }  
  105.   
  106.     return 0;  
  107. }  
  108.   
  109.   
  110. //a struct array to CJSON  
  111. int struct_array_to_cJSON(char *text, people worker[])  
  112. {  
  113.     cJSON *json,*arrayItem,*item,*object;  
  114.     int i;  
  115.   
  116.     for(i=0;i<3;i++)  
  117.     {  
  118.         printf("i=%d, firstName=%s,lastName=%s,email=%s,age=%d,height=%f\n",  
  119.                 i,  
  120.                 worker[i].firstName,  
  121.                 worker[i].lastName,  
  122.                 worker[i].email,  
  123.                 worker[i].age,  
  124.                 worker[i].height);  
  125.     }  
  126.   
  127.     if((text==NULL) || (worker==NULL))  
  128.     {  
  129.         printf("%s: input is invalid",__func__);  
  130.     }  
  131.   
  132.     char * out=NULL;  
  133.     cJSON *root=cJSON_CreateObject();  
  134.   
  135.     if (!root)  
  136.     {  
  137.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  138.         return -1;  
  139.     }  
  140.     else  
  141.     {  
  142.         cJSON *array_person=cJSON_CreateArray();  
  143.   
  144.         for(i=0;i<3;i++)  
  145.         {  
  146.             cJSON *obj_person=cJSON_CreateObject();  
  147.   
  148.             cJSON *item=cJSON_CreateString(worker[i].firstName);  
  149.             cJSON_AddItemToObject(obj_person,"firstName",item);  
  150.   
  151.             item=cJSON_CreateString(worker[i].lastName);  
  152.             cJSON_AddItemToObject(obj_person,"lastName",item);  
  153.   
  154.             item=cJSON_CreateString(worker[i].email);  
  155.             cJSON_AddItemToObject(obj_person,"email",item);  
  156.   
  157.             item=cJSON_CreateNumber(worker[i].age);  
  158.             cJSON_AddItemToObject(obj_person,"age",item);  
  159.   
  160.             item=cJSON_CreateNumber(worker[i].height);  
  161.             cJSON_AddItemToObject(obj_person,"height",item);  
  162.   
  163.             cJSON_AddItemToArray(array_person,obj_person);  
  164.         }  
  165.   
  166.         cJSON_AddItemToObject(root,"people",array_person);  
  167.   
  168.         out=cJSON_Print(root);  
  169.         printf("out:%s\n",out);  
  170.   
  171.         cJSON_Delete(root);  
  172.         if(out!=NULL)  
  173.         {  
  174.             memcpy(text,out,strlen(out));  
  175.             free(out);  
  176.         }  
  177.     }  
  178.   
  179.     return 0;  
  180. }  
  181.   
  182. // create CJSON, write file  
  183. void dofile(char *filename)  
  184. {  
  185.     FILE *f;  
  186.     int len;  
  187.     char data[1024];  
  188.       
  189.     f=fopen(filename,"wb");  
  190.     fseek(f,0,SEEK_END);  
  191.     len=ftell(f);  
  192.     fseek(f,0,SEEK_SET);  
  193.       
  194.     printf("read file %s complete, len=%d.\n",filename,len);  
  195.   
  196. //  char str_name[40];  
  197. //  int ret = str_to_cJSON(data, str_name);  
  198.   
  199.     people worker[3]={  
  200.             {0,"zhong","Jason","bbbb@126.com",0,1.67},  
  201.             {1,"","jadena","jadena@126.com",8,1.17},  
  202.             {2,"zhu","Juliet","cccc@126.com",36,1.55}  
  203.     };  
  204. //  struct_to_cJSON(data, &worker[1]);  
  205.     struct_array_to_cJSON(data, worker);  
  206.   
  207.     fwrite(data,1,strlen(data),f);  
  208.     fclose(f);  
  209.   
  210. }  


来源: http://blog.csdn.net/lintax/article/details/51549345

3.3.使用cJSON解析JSON字符串

          还是在Linux下,使用C语言编程,先实现读文件的功能,然后开始JSON字符串的解析。我们还是一步步来,先从简单的开始,万丈高楼起于平地嘛。  

1,下载源码;

可以从如下网站来下载:https://sourceforge.net/projects/cjson/ 。

2,包含cJSON的源码;

         下载下来,解压后,从里面找到两个文件(cJSON.c、cJSON.h),复制到我们的工程里面。只需在函数中包含头文件(#include “cJSON.h”),然后和cJSON.c一起编译即可使用。 

3,解析一个键值对;  

         首先是一个简单的键值对字符串,要解析的目标如下:

{"firstName":"Brett"}

要进行解析,也就是要分别获取到键与值的内容。我们很容易就能看出键为firstName,值为Brett,可是,使用cJSON怎么解析呢? 

         对于这个简单的例子,只需要调用cJSON的三个接口函数就可以实现解析了,这三个函数的原型如下:

cJSON*cJSON_Parse(const char *value);

cJSON*cJSON_GetObjectItem(cJSON *object,const char *string);

voidcJSON_Delete(cJSON *c); 

下面按解析过程来描述一次:

(1)       首先调用cJSON_Parse()函数,解析JSON数据包,并按照cJSON结构体的结构序列化整个数据包。使用该函数会通过malloc()函数在内存中开辟一个空间,使用完成需要手动释放。

cJSON*root=cJSON_Parse(json_string); 

(2)       调用cJSON_GetObjectItem()函数,可从cJSON结构体中查找某个子节点名称(键名称),如果查找成功可把该子节点序列化到cJSON结构体中。

cJSON*item=cJSON_GetObjectItem(root,"firstName"); 

(3)       如果需要使用cJSON结构体中的内容,可通过cJSON结构体中的valueint和valuestring取出有价值的内容(即键的值)

本例子中,我们直接访问 item->valuestring 就获取到 "Brett" 的内容了。

(4)       通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。

cJSON_Delete(root);

         这样就完成了一次cJSON接口调用,实现了解析工作。使用起来其实也很简单的啊,呵呵。

4,解析一个结构体;        

         接下来,我们来个复杂一点的,解析一个结构体,要解析的目标如下:

{

         "person":

         {

                   "firstName":"z",

                   "lastName":"jadena",

                   "email":"jadena@126.com",

                   "age":8,

                   "height":1.17

         }       

}

看起来比一个键值对复杂多了,我们又需要学习新的接口函数了吗?

         答案是不需要!

         还是那三个函数就可以了。当然,解析的步骤要复杂一些了,下面我按解析过程来描述一次: 

(1)根据JSON串中的对象,我们定义一个相应的结构体如下:

typedefstruct

{

         char firstName[32];

         char lastName[32];

         char email[64];

         int age;

         float height;

} PERSON;

具体的对应关系,一目了然,我就不罗嗦了。让我们直奔主题,解析!     

(2)还是调用cJSON_Parse()函数,解析JSON数据包。

cJSON*root=cJSON_Parse(json_string); 

(3)调用一次cJSON_GetObjectItem()函数,获取到对象person。

cJSON *object=cJSON_GetObjectItem(root,"person"); 

(4)对我们刚取出来的对象person,多次调用cJSON_GetObjectItem()函数,来获取对象的成员。此时要注意,不同的成员,访问的方法不一样:

cJSON*item;

PERSONperson;

item=cJSON_GetObjectItem(object,"firstName");

memcpy(person.firstName,item->valuestring,strlen(item->valuestring));

item=cJSON_GetObjectItem(object,"lastName");

memcpy(person.lastName,item->valuestring,strlen(item->valuestring));

item=cJSON_GetObjectItem(object,"email");

memcpy(person.email,item->valuestring,strlen(item->valuestring));

item=cJSON_GetObjectItem(object,"age");

person.age=item->valueint;

item=cJSON_GetObjectItem(object,"height");

person.height=item->valuedouble;

这样,就获取到了对象的全部内容了。

(5)       通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。

cJSON_Delete(root);

         至此,我们就使用cJSON接口完成了基于结构体的解析工作。 

5,解析结构体数组的JSON串;          

         最后,我们来个更复杂一些的,来解析一个数组,并且数组的成员是结构体!要解析的JSON串如下:

{

"people":[

{"firstName":"z","lastName":"Jason","email":"bbbb@126.com","height":1.67},

{"lastName":"jadena","email":"jadena@126.com","age":8,"height":1.17},

{"email":"cccc@126.com","firstName":"z","lastName":"Juliet","age":36,"height":1.55}

]

         此时,我们真的又需要学习新的接口了,一个是获取数组长度,一个是取数组成员,函数原型如下:

int    cJSON_GetArraySize(cJSON *array);

cJSON*cJSON_GetArrayItem(cJSON *array,int item); 

         由于前面已经实现了结构体的解析,这里我们只需要关注下数组的相关调用即可。 

(1)调用cJSON_Parse()函数,解析JSON数据包。

(2)调用一次cJSON_GetObjectItem()函数,获取到数组people。

(3)对我们刚取出来的数组people,调用cJSON_GetArraySize()函数,来获取数组中对象的个数。然后,多次调用cJSON_GetArrayItem()函数,逐个读取数组中对象的内容。

(4)通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。

         这样,我们就使用cJSON接口完成了结构体数组的解析工作。

详细代码见后文附带例程。        

说明:

本文所附带例程,实现了结构体数组的解析,只是一个学习之作,对于初学JSON使用cJSON接口的同学,可以有些借鉴参考的作用。 

附带例程: 

[cpp]  view plain  copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <sys/types.h>  
  4. #include <stdlib.h>  
  5. #include <unistd.h>  
  6.   
  7. #include "cJSON.h"  
  8.   
  9. typedef struct  
  10. {  
  11.     int id;  
  12.     char firstName[32];  
  13.     char lastName[32];  
  14.     char email[64];  
  15.     int age;  
  16.     float height;  
  17. }people;  
  18.   
  19. void dofile(char *filename);/* Read a file, parse, render back, etc. */  
  20.   
  21. int main(int argc, char **argv)  
  22. {  
  23.   
  24. //  dofile("json_str1.txt");  
  25. //  dofile("json_str2.txt");  
  26.     dofile("json_str3.txt");  
  27.   
  28.     return 0;  
  29. }  
  30.   
  31. //parse a key-value pair  
  32. int cJSON_to_str(char *json_string, char *str_val)  
  33. {  
  34.     cJSON *root=cJSON_Parse(json_string);  
  35.     if (!root)  
  36.     {  
  37.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  38.         return -1;  
  39.     }  
  40.     else  
  41.     {  
  42.         cJSON *item=cJSON_GetObjectItem(root,"firstName");  
  43.         if(item!=NULL)  
  44.         {  
  45.             printf("cJSON_GetObjectItem: type=%d, key is %s, value is %s\n",item->type,item->string,item->valuestring);  
  46.             memcpy(str_val,item->valuestring,strlen(item->valuestring));  
  47.         }  
  48.         cJSON_Delete(root);  
  49.     }  
  50.     return 0;  
  51. }  
  52.   
  53. //parse a object to struct  
  54. int cJSON_to_struct(char *json_string, people *person)  
  55. {  
  56.     cJSON *item;  
  57.     cJSON *root=cJSON_Parse(json_string);  
  58.     if (!root)  
  59.     {  
  60.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  61.         return -1;  
  62.     }  
  63.     else  
  64.     {  
  65.         cJSON *object=cJSON_GetObjectItem(root,"person");  
  66.         if(object==NULL)  
  67.         {  
  68.             printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  69.             cJSON_Delete(root);  
  70.             return -1;  
  71.         }  
  72.         printf("cJSON_GetObjectItem: type=%d, key is %s, value is %s\n",object->type,object->string,object->valuestring);  
  73.   
  74.         if(object!=NULL)  
  75.         {  
  76.             item=cJSON_GetObjectItem(object,"firstName");  
  77.             if(item!=NULL)  
  78.             {  
  79.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  80.                 memcpy(person->firstName,item->valuestring,strlen(item->valuestring));  
  81.             }  
  82.   
  83.             item=cJSON_GetObjectItem(object,"lastName");  
  84.             if(item!=NULL)  
  85.             {  
  86.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  87.                 memcpy(person->lastName,item->valuestring,strlen(item->valuestring));  
  88.             }  
  89.   
  90.             item=cJSON_GetObjectItem(object,"email");  
  91.             if(item!=NULL)  
  92.             {  
  93.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  94.                 memcpy(person->email,item->valuestring,strlen(item->valuestring));  
  95.             }  
  96.   
  97.             item=cJSON_GetObjectItem(object,"age");  
  98.             if(item!=NULL)  
  99.             {  
  100.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valueint=%d\n",item->type,item->string,item->valueint);  
  101.                 person->age=item->valueint;  
  102.             }  
  103.             else  
  104.             {  
  105.                 printf("cJSON_GetObjectItem: get age failed\n");  
  106.             }  
  107.   
  108.             item=cJSON_GetObjectItem(object,"height");  
  109.             if(item!=NULL)  
  110.             {  
  111.                 printf("cJSON_GetObjectItem: type=%d, string is %s, valuedouble=%f\n",item->type,item->string,item->valuedouble);  
  112.                 person->height=item->valuedouble;  
  113.             }  
  114.         }  
  115.   
  116.         cJSON_Delete(root);  
  117.     }  
  118.     return 0;  
  119. }  
  120.   
  121. //parse a struct array  
  122. int cJSON_to_struct_array(char *text, people worker[])  
  123. {  
  124.     cJSON *json,*arrayItem,*item,*object;  
  125.     int i;  
  126.   
  127.     json=cJSON_Parse(text);  
  128.     if (!json)  
  129.     {  
  130.         printf("Error before: [%s]\n",cJSON_GetErrorPtr());  
  131.     }  
  132.     else  
  133.     {  
  134.         arrayItem=cJSON_GetObjectItem(json,"people");  
  135.         if(arrayItem!=NULL)  
  136.         {  
  137.             int size=cJSON_GetArraySize(arrayItem);  
  138.             printf("cJSON_GetArraySize: size=%d\n",size);  
  139.   
  140.             for(i=0;i<size;i++)  
  141.             {  
  142.                 printf("i=%d\n",i);  
  143.                 object=cJSON_GetArrayItem(arrayItem,i);  
  144.   
  145.                 item=cJSON_GetObjectItem(object,"firstName");  
  146.                 if(item!=NULL)  
  147.                 {  
  148.                     printf("cJSON_GetObjectItem: type=%d, string is %s\n",item->type,item->string);  
  149.                     memcpy(worker[i].firstName,item->valuestring,strlen(item->valuestring));  
  150.                 }  
  151.   
  152.                 item=cJSON_GetObjectItem(object,"lastName");  
  153.                 if(item!=NULL)  
  154.                 {  
  155.                     printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  156.                     memcpy(worker[i].lastName,item->valuestring,strlen(item->valuestring));  
  157.                 }  
  158.   
  159.                 item=cJSON_GetObjectItem(object,"email");  
  160.                 if(item!=NULL)  
  161.                 {  
  162.                     printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);  
  163.                     memcpy(worker[i].email,item->valuestring,strlen(item->valuestring));  
  164.                 }  
  165.   
  166.                 item=cJSON_GetObjectItem(object,"age");  
  167.                 if(item!=NULL)  
  168.                 {  
  169.                     printf("cJSON_GetObjectItem: type=%d, string is %s, valueint=%d\n",item->type,item->string,item->valueint);  
  170.                     worker[i].age=item->valueint;  
  171.                 }  
  172.                 else  
  173.                 {  
  174.                     printf("cJSON_GetObjectItem: get age failed\n");  
  175.                 }  
  176.   
  177.                 item=cJSON_GetObjectItem(object,"height");  
  178.                 if(item!=NULL)  
  179.                 {  
  180.                     printf("cJSON_GetObjectItem: type=%d, string is %s, value=%f\n",item->type,item->string,item->valuedouble);  
  181.                     worker[i].height=item->valuedouble;  
  182.                 }  
  183.             }  
  184.         }  
  185.   
  186.         for(i=0;i<3;i++)  
  187.         {  
  188.             printf("i=%d, firstName=%s,lastName=%s,email=%s,age=%d,height=%f\n",  
  189.                     i,  
  190.                     worker[i].firstName,  
  191.                     worker[i].lastName,  
  192.                     worker[i].email,  
  193.                     worker[i].age,  
  194.                     worker[i].height);  
  195.         }  
  196.   
  197.         cJSON_Delete(json);  
  198.     }  
  199.     return 0;  
  200. }  
  201.   
  202. // Read a file, parse, render back, etc.  
  203. void dofile(char *filename)  
  204. {  
  205.     FILE *f;  
  206.     int len;  
  207.     char *data;  
  208.       
  209.     f=fopen(filename,"rb");  
  210.     fseek(f,0,SEEK_END);  
  211.     len=ftell(f);  
  212.     fseek(f,0,SEEK_SET);  
  213.     data=(char*)malloc(len+1);  
  214.     fread(data,1,len,f);  
  215.     fclose(f);  
  216.       
  217.     printf("read file %s complete, len=%d.\n",filename,len);  
  218.   
  219. //  char str_name[40];  
  220. //  int ret = cJSON_to_str(data, str_name);  
  221.   
  222. //  people person;  
  223. //  int ret = cJSON_to_struct(data, &person);  
  224.   
  225.     people worker[3]={{0}};  
  226.     cJSON_to_struct_array(data, worker);  
  227.   
  228.     free(data);  
  229. }  

来源: http://blog.csdn.net/lintax/article/details/50993958

到此,读者可基本掌握cjson的使用,如果仅仅为使用,基本可满足需求。想进一步学习,可继续阅读。

4.cjson源码解析

4.1.cjson基本数据结构

1)文本数据 
cjson处理的数据为json语言写的文本文件。
2)json类型定义
json 的 value 存在这么几种类型: object对象, array数组, string字符串, number数字, true, false, null。
其中对象是一个 key-value 的集合, 而数组是一些 value 的有序列表。
cjson 中在 头文件中定义了 这些类型的数字编号和 cJSON value 的结构体。
/* 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
有网友建议用emnu 来实现类型的定义, 如:
enum {cJSON_False, cJSON_True, cJSON_NULL, cJSON_Number, cJSON_String, cJSON_Array, cJSON_Object, cJSON_IsReference=256};
3)cJSON结构体
/* 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处理数据的主要数据结构,也是其构建链表结构的重要组成元素。

4.2.节点操作

4.2.1.json 内存管理--hook 管理函数

在 c 语言中内存一般是 malloc 和 free 的。
为了方便用户自由的管理内存, cjson 使用 Hook 技术来让使用者可以自定义内存管理函数。
即用户自定义 malloc 和 free.
具体实现方式可以参考下面的代码, 默认使用系统的 malloc 和 free 函数, 用过 cJSON_InitHooks 函数可以替换成用户自定义的 malloc 和 free 函数。
typedef struct cJSON_Hooks {
    void *(*malloc_fn)(size_t sz);
    void (*free_fn)(void *ptr);
} cJSON_Hooks;
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
void cJSON_InitHooks(cJSON_Hooks* hooks) {
    if (!hooks) { /* Reset hooks */
        cJSON_malloc = malloc;
        cJSON_free = free;
        return;
    }
    cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
    cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
注:
利用函数指针,定义自己的处理函数,并且可应用系统函数。

4.2.2.节点操作

在内存管理基础上,可以创建不同类型的节点,这些节点保存的就是json的value值,即key-value键值对。

通过节点操作可以创建的节点连接起来,组成一棵树。

一个json文件可以理解为一棵树形数据结构。

数据存储就是链表操作,就回到了常见数据结构的操作。

这里指的就是节点操作,将节点 a 添加为节点 b 的儿子,将节点 b 从节点 a 的儿子中删除,或者修改节点 a 的值或者查询节点 a 的值等,变为链表基本操作: 增删改查 

4.2.3.创建节点

1)基本操作:
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
    cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
    if (node) memset(node,0,sizeof(cJSON));
    return node;
}
后面操作均在此基础上展开。
2)创建节点操作
/* Create basic types: */
cJSON *cJSON_CreateNull(void)                   {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void)                   {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void)                  {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b)                  {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
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;}
cJSON *cJSON_CreateString(const char *string)   {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void)                  {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void)                 {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count)       {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(const float *numbers,int count)   {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count)  {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateObject(void)为常见操作,
3)附带一个复制字符串操作
static char* cJSON_strdup(const char* str)
{
      size_t len;
      char* copy;
      len = strlen(str) + 1;
      if (!(copy = (char*)cJSON_malloc(len))) return 0;
      memcpy(copy,str,len);
      return copy;
}

4.2.4.删除节点

删除节点很简单, 先删除儿子,然后清理内存即可。

总结一下就是对于 object 和 array 需要先删除儿子,然后删除自己。
对于 字符串, 需要先释放字符串的内存, 再释放自己这块内存。
对于其他节点,直接释放自己这块内存。

/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
    cJSON *next;
    while (c)
    {
        next=c->next;
        if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
        if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
        if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
        cJSON_free(c);
        c=next;
    }
}

4.2.5.添加儿子节点

添加儿子节点有两种情况,一种是给 object 增加儿子, 一种是给 array 增加儿子。

object 和 array 相比, 仅仅多了一个操作 ,即设置 key .

所以我们可以再 object 中设置完 key 之后再调用 给 array 添加儿子的操作来实现给 object 添加儿子。

具体参考胆码。

/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {
    //两个兄弟的指针互相指向对方
    prev->next=item; 
    item->prev=prev;
}
/* Add item to array/object. */
void   cJSON_AddItemToArray(cJSON *array, cJSON *item) {
    cJSON *c=array->child;
    if (!item) return;
    if (!c) {
        array->child=item; //之前没有儿子,直接添加
    } else {
        while (c && c->next) c=c->next; // 先找到最后一个儿子。
        suffix_object(c,item); // 添加儿子, c 是 item 的兄弟
    }
}
void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {
    if (!item) return;
    if (item->string) cJSON_free(item->string);//这个 儿子之前有key, 先清理了。  
    item->string=cJSON_strdup(string); // 设置 key
    cJSON_AddItemToArray(object,item); // 添加儿子
}
实际上上面这两个操作即可满足我们的添加需求了。

但是 cjson 为了我们更方便的使用添加节点的操作, 它又封装了一些操作, 当然使用宏定义封装的。

比如我们平常给 object 增加一个 false 儿子需要这样

cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
现在我们只需要这样

cJSON_AddFalseToObject(object,name)
具体实现方式就是定义一个宏。

而且 cjson 只定义了对象的添加,而没有对数组定义这个宏。

大概原因是那时候, 一般一个数组内的元素的类型都是相同的吧, 不像对象这么灵活。

/* Macros for creating things quickly. */
#define cJSON_AddNullToObject(object,name)      cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name)      cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name)    cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b)    cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n)  cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s)  cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
因此 cjson 还专门为 数组定义了下面的批量创建节点。

/* These utilities create an Array of count items. */
cJSON *cJSON_CreateIntArray(const int *numbers,int count);
cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
cJSON *cJSON_CreateStringArray(const char **strings,int count);
另外, 当我们要添加的节点已经在一个树上的时候, 再向另一个树中添加这个节点时, 这个节点的 pre 和 next 指针会被覆盖。

于是 cjson 又提供了一种引用性添加节点的方法。

简单的说就是在创建一个 item, 新创建的 item 的 value 指针直接指向原来的 value 值, 这样两个 item 就指向了同一个 item 了。

但是这个引用计数是个难题, cjson 也没有处理好, 只能引用一次, 大家可以想象怎么解决。

我们先来看看 cjson 的引用是怎么实现的。

/* Utility for handling references. */
static cJSON *create_reference(cJSON *item) {
cJSON *ref=cJSON_New_Item();
if (!ref) return 0;
memcpy(ref,item,sizeof(cJSON));
ref->string=0;
ref->type|=cJSON_IsReference;
ref->next=ref->prev=0;
return ref;
}
上面的引用计数仅仅存在 type 里面,显示是有问题的。

我们的 value 是保持不变的,所有的引用都指向这个value.

所以我们可以通过一个和 value 类似的东西, 大家都指向这个 东西, 新增加一个引用的时候加1, 释放一个引用的时候减一即可。

这个看着怎么那么像智能指针呢?

4.2.6.删除儿子节点

删除也是从 array 和 object 中删除,实现就比较简洁了。

void  cJSON_DeleteItemFromArray(cJSON *array,int which) {
cJSON_Delete(cJSON_DetachItemFromArray(array,which));
}
void  cJSON_DeleteItemFromObject(cJSON *object,const char *string) {
cJSON_Delete(cJSON_DetachItemFromObject(object,string));
}
Detach 是什么东西呢?

我们把一个节点从 json 树中删除, 但是不释放内存,而是先保留这个节点的指针, 这样储存在这个节点的信息都保留了下来。

接下来我们就可以做很多事了, 合适的时候添加到其他对象中, 合适的时候释放内存。

比如上面的 delete 函数, 就需要真实的删除了, 这个时候我们删除即可。

而 detach 实现也比较简单, 只是少了一步删除操作。
// 节点从双向链表中删除即可
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {
    cJSON *c=array->child;
    while (c && which>0) c=c->next,which--;
    if (!c) return 0;
    if (c->prev) c->prev->next=c->next;
    if (c->next) c->next->prev=c->prev;
    if (c==array->child) array->child=c->next;
    c->prev=c->next=0;
    return c;
}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {
    int i=0;
    cJSON *c=object->child;
    while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;
    if (c) return cJSON_DetachItemFromArray(object,i);
    return 0;
}

4.2.7.查找节点

对于一般类型的item, 我们直接就得到对应的节点.

但是对于 array 和 object , 我们需要查找对应的节点, 所以就需要去查找了。

这个查找算法由 cjson 的储存节点方式决定着。

由于cjson 采用链表储存了, 所以查找当时只能是暴力遍历了。

cJSON *cJSON_GetArrayItem(cJSON *array,int item) {
    cJSON *c=array->child;
    while (c && item>0) item--,c=c->next;
    return c;
}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {
    cJSON *c=object->child;
    while (c && cJSON_strcasecmp(c->string,string)) c=c->next;
    return c;
}

4.2.8.修改节点

我们查找到对应的节点了,就可以对节点进行简单的修改了。

什么是简单的修改呢?

节点的类型不是 array 和 object 都可以算是简单类型,可以直接修改修改其值即可。

但是对于 array 和 object, 我们想给他赋值的话,涉及到释放就得内存这个问题。

下面我们来看看 cjson 的实现代码。

/* Replace array/object items with new ones. */
void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {
    cJSON *c=array->child;
    while (c && which>0) c=c->next,which--;
    if (!c) return;
    newitem->next=c->next;
    newitem->prev=c->prev;
    if (newitem->next) newitem->next->prev=newitem;
    if (c==array->child) array->child=newitem;
    else newitem->prev->next=newitem;
    c->next=c->prev=0;
    cJSON_Delete(c);
}
void   cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem) {
    int i=0;
    cJSON *c=object->child;
    while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;
    if(c) {
        newitem->string=cJSON_strdup(string);
        cJSON_ReplaceItemInArray(object,i,newitem);
    }
}


4.3.解析概述

4.3.1.解析总的调用操作

/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) {
    const char *end=0;
    cJSON *c=cJSON_New_Item();
    ep=0;
    if (!c) return 0;       /* memory fail */
    end=parse_value(c,skip(value));
    if (!end) {
        cJSON_Delete(c);    /* parse failure. ep is set. */
        return 0;
    }
    /* 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;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {
    return cJSON_ParseWithOpts(value,0,0);
}
上面两个函数, 其实对我们有用的只有一句  end=parse_value(c,skip(value));
, 也就是我们只需要了解一下  parse_value  函数即可。
下面是parse_value函数:
/* 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_value 的实现方式很简单, 根据前几个字符来判断写一个类型是什么。如果是 null, false 或 true 设置类型,并返回偏移指针。如果是其他的,则进入对应的函数中。

顺带提一下,用到的一个函数:
/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {
    while (in && *in && (unsigned char)*in<=32) in++;
    return in;
}
skip 用于用于忽略空白,这里跳过了 ascii 值小于 32 

4.3.2.解析字符串

解析字符串时, 对于特殊字符也应该转义,比如 "\\n" 字符应该转换为 '\n' 这个换行符。当然,如果只有特殊字符转换的话,代码不会又这么长, 对于字符串, 还要支持非 ascii 码的字符, 即 utf8字符。这些字符在字符串中会编码为 \uXXXX 的字符串, 我们现在需要还原为 0-255 的一个字符。

static unsigned parse_hex4(const char *str) {
    unsigned h=0;
    if (*str>='0' && *str<='9') h+=(*str)-'0';
    else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
    else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
    else return 0;
    h=h<<4;
    str++;
    if (*str>='0' && *str<='9') h+=(*str)-'0';
    else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
    else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
    else return 0;
    h=h<<4;
    str++;
    if (*str>='0' && *str<='9') h+=(*str)-'0';
    else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
    else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
    else return 0;
    h=h<<4;
    str++;
    if (*str>='0' && *str<='9') h+=(*str)-'0';
    else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
    else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
    else return 0;
    return h;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str) {
    const char *ptr=str+1;
    char *ptr2;
    char *out;
    int len=0;
    unsigned uc,uc2;
    if (*str!='\"') {
        ep=str;    /* not a string! */
        return 0;
    }
    while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++;/* Skip escaped quotes. */
    out=(char*)cJSON_malloc(len+1);/* This is how long we need for the string, roughly. */
    if (!out) return 0;
    ptr=str+1;
    ptr2=out;
    while (*ptr!='\"' && *ptr) {
        if (*ptr!='\\') *ptr2++=*ptr++;
        else {
            ptr++;
            switch (*ptr) {
            case 'b':
                *ptr2++='\b';
                break;
            case 'f':
                *ptr2++='\f';
                break;
            case 'n':
                *ptr2++='\n';
                break;
            case 'r':
                *ptr2++='\r';
                break;
            case 't':
                *ptr2++='\t';
                break;
            case 'u': /* transcode utf16 to utf8. */
                uc=parse_hex4(ptr+1);
                ptr+=4;/* get the unicode char. */
                if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)break;/* check for invalid.*/
                if (uc>=0xD800 && uc<=0xDBFF) {/* UTF16 surrogate pairs.*/
                    if (ptr[1]!='\\' || ptr[2]!='u')break;/* missing second-half of surrogate.*/
                    uc2=parse_hex4(ptr+3);
                    ptr+=6;
                    if (uc2<0xDC00 || uc2>0xDFFF)break;/* invalid second-half of surrogate.*/
                    uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
                }
                len=4;
                if (uc<0x80) len=1;
                else if (uc<0x800) len=2;
                else if (uc<0x10000) len=3;
                ptr2+=len;
                switch (len) {
                case 4:
                    *--ptr2 =((uc | 0x80) & 0xBF);
                    uc >>= 6;
                case 3:
                    *--ptr2 =((uc | 0x80) & 0xBF);
                    uc >>= 6;
                case 2:
                    *--ptr2 =((uc | 0x80) & 0xBF);
                    uc >>= 6;
                case 1:
                    *--ptr2 =(uc | firstByteMark[len]);
                }
                ptr2+=len;
                break;
            default:
                *ptr2++=*ptr;
                break;
            }
            ptr++;
        }
    }
    *ptr2=0;
    if (*ptr=='\"') ptr++;
    item->valuestring=out;
    item->type=cJSON_String;
    return ptr;
}
static unsigned parse_hex4(const char *str)用于解析16进制。

4.3.3.解析数字

数字解析需要考虑科学计数法。
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num) {
    double n=0,sign=1,scale=0;
    int subscale=0,signsubscale=1;
    if (*num=='-') sign=-1,num++;/* Has sign? */
    if (*num=='0') num++;/* is zero */
    if (*num>='1' && *num<='9')don=(n*10.0)+(*num++ -'0');
    while (*num>='0' && *num<='9');/* Number? */
    if (*num=='.' && num[1]>='0' && num[1]<='9') {
        num++;   /* Fractional part? */
        don=(n*10.0)+(*num++ -'0'),scale--;
        while (*num>='0' && *num<='9');
    }
    if (*num=='e' || *num=='E') {/* Exponent? */
        num++;
        if (*num=='+') num++;
        else if (*num=='-') signsubscale=-1,num++;/* With sign? */
        while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');/* Number? */
    }
    n=sign*n*pow(10.0,(scale+subscale*signsubscale));/* number = +/- number.fraction * 10^+/- exponent */
    item->valuedouble=n;
    item->valueint=(int)n;
    item->type=cJSON_Number;
    return num;
}

4.3.4.解析数组

解析数组, 需要先遇到 '[' 这个符号, 然后挨个的读取节点内容, 节点使用 ',' 分隔, ',' 前后还可能有空格, 最后以 ']' 结尾。 我们要编写的也是这样。 先创建一个数组对象, 判断是否有儿子, 有的话读取第一个儿子, 然后判断是不是有 逗号, 有的话循环读取后面的儿子。 最后读取 ']' 即可。
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value) {
    cJSON *child;
    if (*value!='[') {
        ep=value;    /* not an array! */
        return 0;
    }
    item->type=cJSON_Array;
    value=skip(value+1);
    if (*value==']') return value+1;/* empty array. */
    item->child=child=cJSON_New_Item();
    if (!item->child) return 0; /* memory fail */
    value=skip(parse_value(child,skip(value)));/* 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_value(child,skip(value+1)));
        if (!value) return 0;/* memory fail */
    }
    if (*value==']') return value+1;/* end of array */
    ep=value;
    return 0;/* malformed. */
}

4.3.5.解析对象

解析对象和解析数组类似, 只不过对象的一个儿子是个 key-value, key 是字符串, value 可能是任何值, key 和 value 用 ":" 分隔。
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value) {
    cJSON *child;
    if (*value!='{') {
        ep=value;    /* not an object! */
        return 0;
    }
    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;    /* fail! */
        return 0;
    }
    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;    /* fail! */
            return 0;
        }
        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. */
}
注:各种解析之间有相互调用关系。

4.4.json序列化--打印输出或保存

json 序列化也成为把 json 输出出来。

一般有两种输出:格式化输出,压缩输出。

简单的说就是要不要输出一些空白的问题。

/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {
    return print_value(item,0,1);
}
char *cJSON_PrintUnformatted(cJSON *item) {
    return print_value(item,0,0);
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt) {
    char *out=0;
    if (!item) return 0;
    switch ((item->type)&255) {
    case cJSON_NULL:
        out=cJSON_strdup("null");
        break;
    case cJSON_False:
        out=cJSON_strdup("false");
        break;
    case cJSON_True:
        out=cJSON_strdup("true");
        break;
    case cJSON_Number:
        out=print_number(item);
        break;
    case cJSON_String:
        out=print_string(item);
        break;
    case cJSON_Array:
        out=print_array(item,depth,fmt);
        break;
    case cJSON_Object:
        out=print_object(item,depth,fmt);
        break;
    }
    return out;
}

由于基本类型输出的实现比较简单,这里就不多说了,这里只说说输出 对象的实现吧。

假设我们要使用格式化输出, 也就是美化输出。

cjson 的做法不是边分析 json 边输出, 而是预先将要输的内容全部按字符串存在内存中, 最后输出整个字符串。

这对于比较大的 json 来说, 内存就是个问题了。

另外,格式化输出依靠的是节点的深度, 这个也可以优化, 一般宽度超过80 时, 就需要从新的一行算起的。

/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt) {
    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;
    /* Count the number of entries. */
    while (child) numentries++,child=child->next;
    /* Explicitly handle empty object case */
    if (!numentries) {
        out=(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;
    }
    /* 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);
        entries[i++]=ret=print_value(child,depth,fmt);
        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';
        strcpy(ptr,names[i]);
        ptr+=strlen(names[i]);
        *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;
}

5.参考文章.

1. http://ju.outofmemory.cn/entry/105377
2. https://www.cnblogs.com/yank/p/4028266.html
3. http://blog.csdn.net/assholeu/article/details/43037373
4. http://blog.csdn.net/lintax/article/details/51549345













这篇关于cjson介绍-应用-实例-源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1138452

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

zoj3820(树的直径的应用)

题意:在一颗树上找两个点,使得所有点到选择与其更近的一个点的距离的最大值最小。 思路:如果是选择一个点的话,那么点就是直径的中点。现在考虑两个点的情况,先求树的直径,再把直径最中间的边去掉,再求剩下的两个子树中直径的中点。 代码如下: #include <stdio.h>#include <string.h>#include <algorithm>#include <map>#

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、