• 一款轻量级的JSON解析库,用cJSON让你看清王者荣耀


    在这里插入图片描述

    来源:微信公众号「编程学习基地」

    2021年的第一篇文章,带你看清王者荣耀

    JSON是一种轻量级的数据格式,应用广泛。在C/C++应用中也常常作为配置文件或者数据的存储

    JSON语法规则

    JSON对象是一个无序的"名称/值"键值对的集合:

    • 以"{“开始,以”}"结束,允许嵌套使用;
    • 每个名称和值成对出现,名称和值之间使用":"分隔;
    • 键值对之间用","分隔
    • 在这些字符前后允许存在无意义的空白符;

    对于键值,可以有如下值:

    • 一个新的json对象
    • 数组:使用"[“和”]"表示
    • 数字:直接表示,可以是整数,也可以是浮点数
    • 字符串:使用引号"表示
    • 字面值:false、null、true中的一个(必须是小写)

    示例如下:

    [{
    	"ename": 105,
    	"cname": "廉颇",
    	"title": "正义爆轰",
    	"new_type": 0,
    	"hero_type": 3,
    	"skin_name": "正义爆轰|地狱岩魂"
    }, {
    	"ename": 106,
    	"cname": "小乔",
    	"title": "恋之微风",
    	"new_type": 0,
    	"hero_type": 2,
    	"skin_name": "恋之微风|万圣前夜|天鹅之梦|纯白花嫁|缤纷独角兽"
    }]
    
    

    cJSON

    cJSON下载使用

    cJSON是使用ANSI C编写的超轻量级的JSON解析器,因此在C中也常常是不二之选。

    cJSON项目托管在Github上,仓库地址如下:

    https://github.com/DaveGamble/cJSON

    使用Git命令将其拉取到本地:

    git clone https://github.com/DaveGamble/cJSON.git
    

    从Github拉取cJSON源码后,文件非常多,但是其中cJSON的源码文件只有两个:

    • cJSON.h
    • cJSON.c

    使用的时候,只需要将这两个文件复制到工程目录,然后包含头文件cJSON.h即可,如下:

    #include "cJSON.h"
    

    详细使用视频:VS使用cJSON库

    关键数据结构

    cJSON的关键数据结构如下:

    typedef struct cJSON {  //cJSON结构体
           struct cJSON*next,*prev;           /*后驱节点和前驱节点*/
           struct cJSON *child;                   /*孩子节点*/
           int type;                                     /* 键的类型*/
           char *valuestring;                       /*字符串值*/
           int valueint;                                /* 整数值*/
           double valuedouble;                    /* 浮点数值*/
           char *string;                               /* 键的名字*/
    } cJSON;
    

    json是一种组织良好的数据格式,因而JSON中的内容解析后,都可以通过以上数据结构进行处理。

    例如,对于下面的json内容:

    {
        "name":"编程学习基地",
        "site":"https://www.deroy.cn",
        "age":1
    }
    

    解析后,site将会是name的next节点,并且它的键类型是字符串。

    cJSON数据解析

    常用接口函数

    用于将字符串解析成json对象,若失败则返回NULL。

    cJSON *cJSON_Parse(const char *value);
    

    用于获取json对象中的某个节点,若失败,返回NULL,成功则返回该节点对象。

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

    用于释放json对象相关内存。

    void   cJSON_Delete(cJSON *c);
    

    如果JSON数据的值是数组,可以通过下面接口获取 JSON 数组大小和数组里面的 JSON 对象

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

    解析步骤

    • 将JSON文件内容读取到buffer
    • 通过cJSON接口解析buffer中的字符串
    • 获取JSON指定字段

    为了将JSON文件的内容读取到buffer,需要知道文件的大小:

    size_t get_file_size(const char *filepath)
    {
        /*check input para*/
        if(NULL == filepath)
            return 0;
        struct stat filestat;
        memset(&filestat,0,sizeof(struct stat));
        /*get file information*/
        if(0 == stat(filepath,&filestat))
            return filestat.st_size;
        else
            return 0;
    }
    

    然后申请一段内存,将文件中的文本读取到buffer中:

    char *read_file_to_buf(const char *filepath)
    {
        /*check input para*/
        if(NULL == filepath)
        {
            return NULL;
        }
        /*get file size*/
        size_t size = get_file_size(filepath);
        if(0 == size)
            return NULL;
    
        /*malloc memory*/
        char *buf = malloc(size+1);
        if(NULL == buf)
            return NULL;
        memset(buf,0,size+1);
    
        /*read string from file*/
        FILE *fp = fopen(filepath,"r");
        size_t readSize = fread(buf,1,size,fp);
        if(readSize != size)
        {
            /*read error*/
            free(buf);
            buf = NULL;
        }
    
        buf[size] = 0;
        return buf;
    }
    

    再根据前面提到的解析流程,我们的JSON预解析函数如下:

    cJSON *prepare_parse_json(const char *filePath)
    {
        /*check input para*/
        if(NULL == filePath)
        {
            printf("input para is NULL
    ");
            return NULL;
        }
        /*read file content to buffer*/
        char *buf = read_file_to_buf(filePath);
        if(NULL == buf)
        {
            printf("read file to buf failed
    ");
            return NULL;
        }
        /*parse JSON*/
        cJSON *pTemp = cJSON_Parse(buf);
        free(buf);
        buf = NULL;
        return pTemp;
    }
    

    解析示例

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/stat.h>
    #include<string.h>
    #include"cJSON.h"
    
    size_t get_file_size(const char *filepath)
    {
        /*check input para*/
        if(NULL == filepath)
            return 0;
        struct stat filestat;
        memset(&filestat,0,sizeof(struct stat));
        /*get file information*/
        if(0 == stat(filepath,&filestat))
            return filestat.st_size;
        else
            return 0;
    }
    
    char *read_file_to_buf(const char *filepath)
    {
        /*check input para*/
        if(NULL == filepath)
        {
            return NULL;
        }
        /*get file size*/
        size_t size = get_file_size(filepath);
        if(0 == size)
            return NULL;
            
        /*malloc memory*/
        char *buf = malloc(size+1);
        if(NULL == buf)
            return NULL;
        memset(buf,0,size+1);
        
        /*read string from file*/
        FILE *fp = fopen(filepath,"r");
        size_t readSize = fread(buf,1,size,fp);
        if(readSize != size)
        {
            /*read error*/
            free(buf);
            buf = NULL;
        }
    
        buf[size] = 0;
        return buf;
    }
    /**/
    cJSON *prepare_parse_json(const char *filePath)
    {
        /*check input para*/
        if(NULL == filePath)
        {
            printf("input para is NULL
    ");
            return NULL;
        }
        /*read file content to buffer*/
        char *buf = read_file_to_buf(filePath);
        if(NULL == buf)
        {
            printf("read file to buf failed
    ");
            return NULL;
        }
        /*parse JSON*/
        cJSON *pTemp = cJSON_Parse(buf);
        free(buf);
        buf = NULL;
        return pTemp;
    }
    int main(void)
    {
        char *filename = "herolist.json";
        cJSON *pJson = NULL;
    	cJSON *pTemp = NULL;
        cJSON *pVal = NULL;
        /*创建cJSON对象*/
        pJson = prepare_parse_json(filename);
        if(NULL == pJson)
        {
            printf("parse json failed
    ");
            return -1;
        }
        /*获取cJSON数组数量*/
    	int num = cJSON_GetArraySize(pJson);
        /*遍历每一个cJSON数组元素*/
    	for(int index=0;index<num;index++)
    	{
            /*获取cJSON数组中的第index个cJSON对象*/
    		pTemp = cJSON_GetArrayItem(pJson,index);
            
    		/*获取cJSON对象中的key值为ename的对象*/
    		pVal = cJSON_GetObjectItem(pTemp,"ename");
    		printf("ename:%d
    ",pVal->valueint);
    		
    		pVal = cJSON_GetObjectItem(pTemp,"cname");
    		printf("cname:%s
    ",pVal->valuestring);
    		
    		pVal = cJSON_GetObjectItem(pTemp,"title");
    		printf("title:%s
    ",pVal->valuestring);
    		
    		pVal = cJSON_GetObjectItem(pTemp,"new_type");
    		printf("new_type:%d
    ",pVal->valueint);
    		
    		pVal = cJSON_GetObjectItem(pTemp,"hero_type");
    		printf("hero_type:%d
    ",pVal->valueint);
    		
    		pVal = cJSON_GetObjectItem(pTemp,"skin_name");
    		printf("skin_name:%s
    
    ",pVal->valuestring);
    		printf("====================================
    
    ");
    	}
        
        /*释放内存*/
        cJSON_Delete(pJson);
        pJson = NULL;
        return 0;
    }
    
    gcc -o mian main.c cJSON.c
    

    windows下VC6.0,VS也可以运行,但是因为编码格式问题,我另写了一套程序

    为了让输出看起来舒服,改了点格式,输出如下(删减),源码获取发送关键字【王者荣耀】

    |ename	|cname		|title		|new_type	|hero_type	|skin_name|
    -----------------------------------------------------------------------------------------------------------
    |105	|廉颇		|正义爆轰	|0		|3		|正义爆轰|地狱岩魂|
    -----------------------------------------------------------------------------------------------------------
    |106	|小乔		|恋之微风	|0		|2		|恋之微风|万圣前夜|天鹅之梦|纯白花嫁|缤纷独角兽|
    -----------------------------------------------------------------------------------------------------------
    |107	|赵云		|苍天翔龙	|0		|1		|苍天翔龙|忍●炎影|未来纪元|皇家上将|嘻哈天王|白执事|引擎之心|
    -----------------------------------------------------------------------------------------------------------
    |108	|墨子		|和平守望	|0		|2		|和平守望|金属风暴|龙骑士|进击墨子号|
    -----------------------------------------------------------------------------------------------------------
    

    JSON数据封装

    封装方法

    封装JSON数据的过程,其实就是创建链表和向链表中添加节点的过程。

    首先来讲述一下链表中的一些术语:

    • 头指针:指向链表头结点的指针;
    • 头结点:不存放有效数据,方便链表操作;
    • 首节点:第一个存放有效数据的节点;
    • 尾节点:最后一个存放有效数据的节点;

    封装步骤

    明白了这几个概念之后,我们开始讲述创建一段完整的JSON数据,即如何创建一条完整的链表。

    • ① 创建头指针:
     cJSON* cjson_test = NULL;
    
    • ② 创建头结点,并将头指针指向头结点:
    cjson_test = cJSON_CreateObject();
    
    • ③ 尽情的向链表中添加节点:
    /* 添加一个值为 null 的布尔类型的JSON数据(添加一个链表节点) */
    cJSON_AddNullToObject(cJSON * const object, const char * const name);
    /* 添加一个值为 true 的布尔类型的JSON数据(添加一个链表节点) */
    cJSON_AddTrueToObject(cJSON * const object, const char * const name);
    /* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
    cJSON_AddFalseToObject(cJSON * const object, const char * const name);
    /* 添加一个值为布尔类型的JSON数据 0:false 非0:true (添加一个链表节点) */
    cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
    /* 添加一条数值类型的JSON数据(添加一个链表节点) */
    cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
    /* 添加一条字符串类型的JSON数据(添加一个链表节点) */
    cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
    /* 添加一行数据(添加一个链表节点) */
    cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
    /* 添加一个空对象(添加一个链表节点) */
    cJSON_AddObjectToObject(cJSON * const object, const char * const name);
    /* 添加一个空数组(添加一个链表节点) */
    cJSON_AddArrayToObject(cJSON * const object, const char * const name);
    /* 添加一个嵌套的JSON对象/数组(添加一个链表节点) */
    cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
    

    cJSON还提供了将JSON对象转换成字符串输出到终端

    char *cJSON_Print(const cJSON *item);
    

    封装示例

    #include <stdio.h>
    #include "cJSON.h"
    int main(void)
    {
        cJSON* cjson_test = NULL;
        cJSON* cjson_address = NULL;
        cJSON* cjson_skill = NULL;
        char* str = NULL;
    
        /* 创建一个JSON数据对象(链表头结点) */
        cjson_test = cJSON_CreateObject();
    
        /* 添加一个值为 null 的布尔类型的JSON数据(添加一个链表节点) */
        cJSON_AddNullToObject(cjson_test, "null_test");
    
        /* 添加一个值为 true 的布尔类型的JSON数据(添加一个链表节点) */
        cJSON_AddTrueToObject(cjson_test,"true_test");
        /* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
        cJSON_AddFalseToObject(cjson_test, "false_test");
        /* 添加一个值为布尔类型的JSON数据 0:false 非0:true (添加一个链表节点) */
        cJSON_AddBoolToObject(cjson_test, "bool_test", 0);
    
        /* 添加一条整数类型的JSON数据(添加一个链表节点) */
        cJSON_AddNumberToObject(cjson_test, "int_test", 22);
    
        /* 添加一条浮点类型的JSON数据(添加一个链表节点) */
        cJSON_AddNumberToObject(cjson_test, "double_test", 55.5);
    
        /* 添加一条字符串类型的JSON数据(添加一个链表节点) */
        cJSON_AddStringToObject(cjson_test, "str_test", "我是字符串");
    
        /* 添加一行任意数据(添加一个链表节点) */
        cJSON_AddRawToObject(cjson_test, "key", "任意数据");
    
        /* 添加一个空对象(添加一个链表节点) */
        cJSON_AddObjectToObject(cjson_test, "objet");
    
        /* 添加一个嵌套的JSON对象(添加一个链表节点) */
        cjson_address = cJSON_CreateObject();
        cJSON_AddStringToObject(cjson_address, "country", "China");
        cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);
        cJSON_AddItemToObject(cjson_test, "address", cjson_address);
    
        /* 添加一个空数组(添加一个链表节点) */
        cJSON_AddArrayToObject(cjson_test, "Array");
    
        /* 添加一个数组类型的JSON数据(添加一个链表节点) */
        cjson_skill = cJSON_CreateArray();
        cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));
        cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C++" ));
        cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));
        cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java"));
        cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);
    
        /* 打印JSON对象(整条链表)的所有数据 */
        str = cJSON_Print(cjson_test);
        printf("%s
    ", str);
    
        return 0;
    }
    

    输出结果:

    {
    	"null_test":	null,
    	"true_test":	true,
    	"false_test":	false,
    	"bool_test":	false,
    	"int_test":	22,
    	"double_test":	55.5,
    	"str_test":	"我是字符串",
    	"key":	任意数据,
    	"objet":	{
    	},
    	"address":	{
    		"country":	"China",
    		"zip-code":	111111
    	},
    	"Array":	[],
    	"skill":	["C", "C++", "Python", "Java"]
    }
    

    完整代码:

    链接:https://pan.baidu.com/s/1Pej2aKKPW5d0_cmUMgm5HQ
    提取码:18re

  • 相关阅读:
    leetcode 二进制求和 python
    leetcode 66.加一 python3
    django自定义simple_tag和filter
    deepin Gtk-WARNING **: 无法在模块路径中找到主题引擎:“adwaita”
    MySQL ERROR 1698 (28000): Access denied for user 'root'@'localhost'
    deepin 安装scrapy安装出错:fatal error: Python.h 的解决办法
    Bootstrap的$(...).modal is not a function错误
    linux 安装mysql5.6
    linux之主机名bogon问题详解
    linux开启代理转发功能
  • 原文地址:https://www.cnblogs.com/deroy/p/14387628.html
Copyright © 2020-2023  润新知