说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  CSDN-cJSON使用详细教程 | 一个轻量级C语言JSON解析器
  JSON的简单介绍以及C语言的JSON库使用
  JSON的简单介绍&cJSON库使用(一)

cJSON数据结构

typedef struct cJSON
{
    struct cJSON *next;	   		 //遍历数组或对象链的后向链表指针
    struct cJSON *prev;	   		 //遍历数组或对象链的前向链表指针
    struct cJSON *child;		 //数组或对象链的孩子节点
    
    int type;					 //key的类型

    char *valuestring;			 //字符串值	
    int valueint;				 //整数值	
    double valuedouble;			 //浮点数值	

    char *string;				 //key的名字
} cJSON;

  从以上代码可看出,cJSON设计思想是:链表。
  数据指向:
    next指向下一节点,prev指向上一节点,child指向子节点(故:对象/数组是可以嵌套,指向新链表)
  数据存储:
    string表示key的名称,type表示key的类型,valuestring、valueint、valuedouble则表示对应type类型的变量。
  PS:
    一个结点表示 数组的一个元素/对象的一个字段。

cJSON 类型

  不管是数值类型、字符串类型或者对象类型等都使用该结构体,类型信息通过标识符 type来进行判断,cJSON总共定义了7种类型:

#define cJSON_Invalid (0)		//表示一个无效的不包含任何值的项。如果将项设置为所有零字节,则自动具有此类型。
#define cJSON_False  (1 << 0)	//表示一个错误的布尔值。您还可以使用cJSON_IsBool检查布尔值。
#define cJSON_True   (1 << 1)	//表示一个正确的布尔值。您还可以使用cJSON_IsBool检查布尔值。
#define cJSON_NULL   (1 << 2)	//表示一个空值。
#define cJSON_Number (1 << 3)	//表示一个数字值。该值在valuedouble和valueint中以double形式存储。如果数值超出了整数的范围,则valueint使用INT_MAX或INT_MIN。
#define cJSON_String (1 << 4)	//表示一个字符串值。它以零终止字符串的形式存储在valuestring中。
#define cJSON_Array  (1 << 5)	//表示一个数组值。这是通过将child指向表示数组中值的cJSON项链表来实现的。元素使用next和prev链接在一起,其中第一个元素是prev。next == NULL和最后一个元素next == NULL。
#define cJSON_Object (1 << 6)	//表示一个对象值。对象的存储方式与数组相同,唯一的区别是对象中的项将它们的键存储为string。
#define cJSON_Raw    (1 << 7) 	//表示任何类型的JSON,它存储在valuestring中以零结尾的字符数组中。例如,可以使用它来避免反复打印相同的静态JSON以节省性能。cJSON在解析时永远不会创建这种类型。还要注意,cJSON不检查它是否是有效的JSON。
另外还有以下两个标志:
cJSON_IsReference:指定子元素所指向的项和/或valuestring不属于这个项,它只是一个引用。所以cJSON_Delete和其他函数将只释放这个项,而不是它的子元素/valuestring。
cJSON_StringIsConst:这意味着字符串指向常量字符串。这意味着cJSON_Delete和其他函数不会尝试释放字符串。

一、构建cJSON

  对于每个值类型,都有一个cJSON_Create xxx可用于创建该类型项的函数。所有这些都将分配一个cJSON结构,稍后可以用cJSON_Delete()删除它。请注意,您必须在某些时候删除它们,否则将导致内存泄漏。

重要提示:
  如果你已经添加了一个项到一个数组或一个对象,你不能删除它与cJSON_Delete。将它添加到数组或对象中会转移它的所有权,这样当数组或对象被删除时,它也会被删除。您还可以使用cJSON_SetValuestring()来更改cJSON_String()valuestring,而不需要手动释放前面的valuestring

(1)创建:

①创建任何类型的JSON (cJSON_CreateRaw)
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
②创建对象的JSON
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);							//创建一个空对象

1.创建:
  cJSON_CreateObject()创建一个空对象。
  cJSON_CreateObjectReference()创建一个不“拥有”其内容的对象,因此它的内容不会被cJSON_Delete删除。
2.添加项:
  cJSON_AddItemToObject()向对象添加项.
  cJSON_AddItemToObjectCS()向名称为常量或引用(项目的keycJSON结构中的字符串)的对象添加一个项目,这样它就不会被cJSON_Delete()释放。
  cJSON_AddItemReferenceToArray(),可添加一个元素作为对另一个对象、数组或字符串的引用。这意味着cJSON_Delete()不会删除该项目的子属性或valuestring属性,因此如果它们已经在其他地方使用,则不会发生双重释放。
3.取出项:
  cJSON_DetachItemFromObjectCaseSensitive()从一个对象中取出一个项,会返回分离的项,所以一定要把它分配给一个指针,否则你会有一个内存泄漏。
4.删除项:
  cJSON_DeleteItemFromObjectCaseSensitive()删除项目.工作方式类似于cJSON_DetachItemFromObjectCaseSensitive(),然后是cJSON_Delete()
  cJSON_ReplaceItemInObjectCaseSensitive()cJSON_ReplaceItemViaPointer()在适当的位置替换对象中的项.若失败,cJSON_ReplaceItemViaPointer()将返回0。这在内部所做的是分离旧的项,删除它并在其位置插入新项。
5.获取大小:
  
cJSON_GetArraySize()获得对象的大小.(这样可以工作,因为在内部对象被存储为数组。)
6.访问项:
  cJSON_GetObjectItemCaseSensitive()访问一个对象中的一个项
7.遍历:
  要遍历一个对象,可以使用cJSON_ArrayForEach宏,方法与使用数组相同。
8.其他:
  cJSON还提供了方便的帮助函数,可以快速创建新项并将其添加到对象中,比如cJSON_AddNullToObject()。它们返回一个指向新项的指针,如果失败则返回NULL

③创建数组的JSON
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);

1.创建:
  cJSON_CreateArray()创建一个空数组。
  cJSON_CreateArrayReference()创建一个不“拥有”其内容的数组,所以它的内容不会被cJSON_Delete()删除。
2.添加项:
  cJSON_AddItemToArray()将项添加到数组中,将项追加到末尾。
  cJSON_AddItemReferenceToArray()可将一个元素添加为另一个项、数组或字符串的引用。这意味着cJSON_Delete将不会删除那些项的子属性或valuestring属性,因此,如果它们已经在其他地方使用了,就不会发生重复释放。
  cJSON_InsertItemInArray(),中间插入项,它将在给定的基于0的索引处插入一个项,并将所有现有项向右移动。
3.取出项:
  cJSON_DetachItemFromArray(),从一个给定索引的数组中取出一个项目并继续使用它,它将返回分离的项目,所以一定要将它分配给一个指针,否则您将有内存泄漏。
4.删除项:
  cJSON_DeleteItemFromArray()删除项目。它的工作原理类似于cJSON_DetachItemFromArray(),但是通过cJSON_Delete删除分离的项目。
5.替换项:
  cJSON_ReplaceItemInArray()用索引/cJSON_ReplaceItemViaPointer()给定元素指针,在适当的位置替换数组中的项.若cJSON_ReplaceItemViaPointer()失败,它将返回0。这在内部做的是分离旧项、删除它并在其位置插入新项。
6.获取大小:
  cJSON_GetArraySize()获得数组的大小。使用cJSON_GetArrayItem()获取给定索引处的元素。
7.遍历:
  因为数组存储为一个链表,通过迭代索引效率低下(O (n²)),所以你可以使用cJSON_ArrayForEach宏遍历一个数组在O (n)时间复杂度。

④创建基础类型的JSON
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);

(2)删除

释放cJSON
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);  //将JSON结构所占用的数据空间释放 

二、解析cJSON

①解析cJSON
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);

  cJSON_Parse():对给定以零结尾的字符串中的一些JSON进行解析。
  cJSON_ParseWithLength():给定一个字符串中的一些JSON(无论是否终止为0)进行解析。
1.空间分配:
  将解析JSON并分配一个表示它的cJSON项树。一旦它返回,将完全负责在与cJSON_Delete()一起使用后对它进行释放。分配器默认是mallocfree,但是可以使用cJSON_InitHooks()(全局)更改。
2.发生错误:
  cJSON_GetErrorPtr():访问指向输入字符串中错误位置的指针。
  注意:这可能会在多线程场景中产生竞争条件,在这种情况下,最好使用cJSON_ParseWithOpts()return_parse_end。默认情况下,解析后的JSON之后的输入字符串中的字符不会被视为错误。
3.更多选项
  若想要更多的选项,使用cJSON_ParseWithOpts()return_parse_end返回一个指针,指向输入字符串中的JSON结尾或错误发生的位置(从而以线程安全的方式替换cJSON_GetErrorPtr()require_null_ended,如果设置为1,那么如果输入字符串包含JSON之后的数据,则会导致错误。
  若你想要更多的设置缓冲区长度的选项,可以使用cJSON_ParseWithLengthOpts()

②输出JSON
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);

  cJSON_Print()给定一个cJSON项树,将它们打印为字符串(将使用空白来打印格式)
它将分配一个字符串并将树的JSON表示形式打印到其中。一旦它返回,您就完全有责任在与分配器一起使用后重新分配它。(通常是免费的,取决于cJSON_InitHooks设置了什么)。
  cJSON_PrintUnformatted ()打印没有格式,则可使用该函数。
  cJSON_PrintBuffered ():若对结果字符串的大小有一个大致的概念,则可使用该函数。
  cJSON_PrintPreallocated()避免这些动态缓冲区分配。它接受一个缓冲区的指针打印到它的长度。如果达到该长度,打印将失败并返回0。如果成功,则返回1。注意,您应该提供比实际需要更多的5个字节,因为cJSON在估计所提供的内存是否足够时不是100%准确的。
  

③其他

1.从object中获取item “string”(根据键找json结点)

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

2.判断是否有key是string的项 如果有返回1 否则返回0

CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);

3 . 返回数组(或对象)中的项数

CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);

4.从数组“array”中检索项目编号“index”(根据数组下标index取array数组结点的第index个成员 返回该成员节点)

CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);

5.遍历数组

#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)

实验:

实验一:构建json,并将其保存到文件中。

创建文件:creat_json.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cJSON.h"
#define CFGFILE "creat_json.json"  

//构建以下json
// {
// 	"name":"王小二",						 //字符串
// 	"age":18,						       //数值
// 	"favorite_food":["dumplings","ribs"],	              //数组
// 	"has_girlfriend":false,					//布尔值
// 	"car":null,							//null
// 	"address":							//对象
// 	{
// 		"country":"China",
// 		"city":"fuzhou",
// 		"postcode":350000
// 	}
// }
char *buf =  NULL;
void test()
{
       cJSON *object = cJSON_CreateObject();                   //创建一个空对象
 
       cJSON *name =  cJSON_CreateString("王小二");             //创建基础类:string,并添加到对象中  
       cJSON_AddItemToObject(object, "name", name);

       cJSON *age =  cJSON_CreateNumber(18);                   //创建基础类:number,并添加到对象中  
       cJSON_AddItemToObject(object, "age", age);

       cJSON *array =  cJSON_CreateArray();                    //创建数组:array ,并添加到对象中  
       cJSON_AddItemToObject(object, "favorite_food", array);  
       cJSON *dumplings =  cJSON_CreateString("dumplings");    //创建基础类:string ,并添加到数组中    
       cJSON_AddItemToArray(array, dumplings);                 
       cJSON *ribs =  cJSON_CreateString("ribs");              //创建基础类:string ,并添加到数组中              
       cJSON_AddItemToArray(array, ribs);

       cJSON *has_girlfriend =  cJSON_CreateFalse();           //创建基础类:false,并添加到对象中  
       cJSON_AddItemToObject(object, "has_girlfriend", has_girlfriend);

       cJSON *car = cJSON_CreateNull();                        //创建基础类:null,并添加到对象中 
       cJSON_AddItemToObject(object, "car", car);

       cJSON *address =cJSON_CreateObject(); 
       cJSON_AddItemToObject(object, "address", address);

       cJSON *country =  cJSON_CreateString("China");             //创建基础类:string,并添加到对象中  
       cJSON_AddItemToObject(address, "country", country);
       
       cJSON *city =  cJSON_CreateString("fuzhou");             //创建基础类:string,并添加到对象中  
       cJSON_AddItemToObject(address, "city", city);

       cJSON *postcode =  cJSON_CreateNumber(350000);             //创建基础类:string,并添加到对象中  
       cJSON_AddItemToObject(address, "postcode", postcode);

 //将内容存到CFGFILE文件中      
       FILE *fp = fopen(CFGFILE,"w");
       buf = cJSON_Print(object);
       fwrite(buf,strlen(buf),1,fp);
       fclose(fp);
       cJSON_Delete(object);
}


int main()
{
    test();
    printf("%s\n",buf);    //显示屏上查看对应的数据
}

执行指令
  gcc creat_json.c -o creat -lcjsongcc creat_json.c cJSON.c -I ./ -o creat

实验二:解析文件
文件1:conf_s_1.json

{
    "name": "T1","type": "s1"
}

文件2:conf_s_2.json

{
    "T": [
    {"name": "T1","type": "s1"},
    {"name": "T2","type": "s2"}
    ]
}
#include <stdlib.h>
#include <stdio.h>
#include "cJSON.h"
#define CFGFILE1 "conf_s_1.json"  
#define CFGFILE2 "conf_s_2.json"           



void test1(){
//将文本内容存到字符串data中
       //打开文件
       FILE* file = fopen(CFGFILE1,"rb");
       //获得文件长度
       fseek(file,0,SEEK_END);
       long len=ftell(file);
       fseek(file,0,SEEK_SET);
       //开辟内存,将文件内容读取到data中
       char* data = (char*)malloc(len+1);            
       fread(data,1,len,file);
       //关闭文件
       fclose(file);
       
       cJSON* root = cJSON_Parse(data);                         //将cJSON实体呈现为用于传输/存储的文本

       cJSON* name_json = cJSON_GetObjectItem(root, "name");    //从对象中获取项目“string”。不区分大小写
       char* name = cJSON_Print(name_json);                     //将cJSON实体呈现为用于传输/存储的文本,而不进行任何格式化  
       printf("name:%s\n", name);
       free(name);     

       cJSON* type_json = cJSON_GetObjectItem(root, "type");    //从对象中获取项目“string”。不区分大小写
       char* type = cJSON_Print(type_json);                     //将cJSON实体呈现为用于传输/存储的文本,而不进行任何格式化        
       printf("type:%s\n", type);
       free(type);
       
       cJSON_Delete(root);                                     //删除一个cJSON实体和所有子实体
       free(data);                                             //删除内存
}

void test2(){
//将文本内容存到字符串data中
       //打开文件
       FILE* file = fopen(CFGFILE2,"rb");
       //获得文件长度
       fseek(file,0,SEEK_END);
       long len=ftell(file);
       fseek(file,0,SEEK_SET);
       //开辟内存,将文件内容读取到data中
       char* data = (char*)malloc(len+1);            
       fread(data,1,len,file);
       //关闭文件
       fclose(file);

       cJSON* root = cJSON_Parse(data);
     
       cJSON* OBJroot=cJSON_GetObjectItem(root, "T");
           
       int array_size = cJSON_GetArraySize(OBJroot);

       for (int i = 0; i < array_size; i++) {
              printf("i:%d\n", i);
              cJSON *object = cJSON_GetArrayItem(OBJroot, i);
              cJSON *name_json = cJSON_GetObjectItem(object, "name");
              char *name = cJSON_Print(name_json);    //将JSON结构体打印到字符串中 需要自己释放
              printf("name:%s\n", name);
              free(name);
              
              cJSON *type_json = cJSON_GetObjectItem(object, "type");
              char *type = cJSON_Print(type_json);    //将JSON结构体打印到字符串中 需要自己释放
              printf("type:%s\n", type);
              free(type);
              
       }
       cJSON_Delete(root);
       free(data);
}

int main()
{
       test1();
       //test2();   
}

实验三:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"cJSON.h"

int main(void)
{
    char *string = "{\"family\":[\"father\",\"mother\",\"brother\",\"sister\",\"somebody\"]}";
    //从缓冲区中解析出JSON结构
    cJSON *json = cJSON_Parse(string);
    cJSON *node = NULL;
    //cJOSN_GetObjectItem 根据key来查找json节点 若果有返回非空
    node = cJSON_GetObjectItem(json,"family");
    if(node == NULL)
    {
        printf("family node == NULL\n");
    }
    else
    {
        printf("found family node\n");
    }
    node = cJSON_GetObjectItem(json,"famil");
    if(node == NULL)
    {
                printf("famil node == NULL\n");
        }
    else
        {
                printf("found famil node\n");
        }
    //判断是否有key是string的项 如果有返回1 否则返回0
    if(1 == cJSON_HasObjectItem(json,"family"))
    {
        printf("found family node\n");
    }
    else
    {
        printf("not found family node\n");
    }
    if(1 == cJSON_HasObjectItem(json,"famil"))
        {
                printf("found famil node\n");
        }
        else
        {
                printf("not found famil node\n");
        }

    node = cJSON_GetObjectItem(json,"family");
    if(node->type == cJSON_Array)
    {
        printf("array size is %d",cJSON_GetArraySize(node));
    }
    //非array类型的node 被当做array获取size的大小是未定义的行为 不要使用
    cJSON *tnode = NULL;
    int size = cJSON_GetArraySize(node);
    int i;
    for(i=0;i<size;i++)
    {
        tnode = cJSON_GetArrayItem(node,i);
        if(tnode->type == cJSON_String)
        {
            printf("value[%d]:%s\n",i,tnode->valuestring);
        }
        else
        {
            printf("node' type is not string\n");
        }
    }

    cJSON_ArrayForEach(tnode,node)
    {
        if(tnode->type == cJSON_String)
        {
            printf("int forEach: vale:%s\n",tnode->valuestring);
        }
        else
        {
            printf("node's type is not string\n");
        }
    }
    return 0;
}
GitHub 加速计划 / cj / cJSON
10.45 K
3.16 K
下载
Ultralightweight JSON parser in ANSI C
最近提交(Master分支:2 个月前 )
424ce4ce This reverts commit 5b502cdbfb21fbe5f6cf9ffbd2b96e4281a741e6. Related to #860 4 个月前
32497300 - 5 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐