cjson解析和打包

一、json

JSON(JavaScript Object Notation,JavaScript对象表示法,读作/ˈdʒeɪsən/)是一种由道格拉斯·克罗克福特构想和设计、轻量级的数据交换语言,该语言以易于让人阅读的文字为基础,用来传输由属性值或者序列性的值组成的数据对象。尽管JSON是JavaScript的一个子集,但JSON是独立于语言的文本格式,并且采用了类似于C语言家族的一些习惯。
JSON的基本数据类型:

数值:十进制数,不能有前导0,可以为负数,可以有小数部分。还可以用e或者E表示指数部分。不能包含非数,如NaN。不区分整数与浮点数。JavaScript用双精度浮点数表示所有数值。

字符串:以双引号""括起来的零个或多个Unicode码位。支持反斜杠开始的转义字符序列。

布尔值:表示为true或者false。

值的有序列表(array):有序的零个或者多个值。每个值可以为任意类型。序列表使用方括号[,]括起来。元素之间用逗号,分割。形如:[value, value]
对象(object):一个无序的“键-值对”(pair),其中键是字符串。建议但不强制要求对象中的键是独一无二的。对象以花括号{开始,并以}结束。键-值对之间使用逗号分隔。键与值之间用冒号:分割。

null类型:值写为null

token(6种标点符号、字符串、数值、3种字面量)之间可以存在有限的空白符并被忽略。四个特定字符被认为是空白符:空格符、水平制表符、回车符、换行符。空白符不能出现在token内部(但空格符可以出现在字符串内部)。JSON标准不允许有字节序掩码,不提供注释的句法。 一个有效的JSON文档的根节点必须是一个对象或一个数组。
具体格式的定义大家可以参考这个链接https://tools.ietf.org/html/rfc4627
举例:

{
     "firstName": "John",
     "lastName": "Smith",
     "sex": "male",
     "age": 25,
     "address": 
     {
         "streetAddress": "21 2nd Street",
         "city": "New York",
         "state": "NY",
         "postalCode": "10021"
     },
     "phoneNumber": 
     [
         {
           "type": "home",
           "number": "212 555-1234"
         },
         {
           "type": "fax",
           "number": "646 555-4567"
         }
     ]
 }

json的[]代表着数组的格式,数组每个{}中间的数据就是数组中的一个元素也就是一个对象,json文件的后缀名为:.json。

二、cjson
1、认识cjson

上面学习了json数据格式的定义,下面我们就来学习下如何使用cjson库。cjson能够帮我们解析和打包,这样就给开发人员很大的便利了,我们首先下载好cjson库
git clone https://github.com/arnoldlu/cJSON.git
在这里插入图片描述
在这里插入图片描述
这样我们就有了cjson的头文件和c文件了,接下来我们可以了解下cjson的大致内容和重要函数的接口,整个库里最重要的就是存储信息的这个结构体


typedef struct cJSON 
{     //cJSON结构体
       struct cJSON *next,*prev;               /* 遍历数组或对象链的前向或后向链表指针*/
       struct cJSON *child;                   /* 数组或对象的孩子节点*/
       int type;                              /* key的类型*/
       char *valuestring;                     /* 字符串值*/
       int valueint;                          /* 整数值*/
       double valuedouble;                    /* 浮点数值*/
       char *string;                          /* key的名字*/
} cJSON;

cJSON对象的实现采用了树形结构,每个对象是树的一个节点,每个节点由cJSON这个结构体组成,对象中的元素也由cJSON这个结构体组成。同一层的对象和元素是双向链表结构,由next和prev指针链接。不同层的对象或元素由child指针链接起来。type表示对象或元素类型,string表示对象或节点的名称。元素的值存储在valuestring, valueint和valuedouble中。

cjson常用的函数

cJSON *cJSON_Parse(const char *value);
 
作用:将一个JSON数据包,按照cJSON结构体的结构序列化整个数据包,并在堆中开辟一块内存存储cJSON结构体
返回值:成功返回一个指向内存块中的cJSON的指针,失败返回NULL

cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
 
作用:获取JSON字符串字段值
返回值:成功返回一个指向cJSON类型的结构体指针,失败返回NUL

char  *cJSON_Print(cJSON *item);
 
作用:将cJSON数据解析成JSON字符串,并在堆中开辟一块char*的内存空间存储JSON字符串
返回值:成功返回一个char*指针该指针指向位于堆中JSON字符串,失败返回NULL
void  cJSON_Delete(cJSON *c);
 
作用:释放位于堆中cJSON结构体内存
返回值:无
void cJSON_AddItemToArray(cJSON *array, cJSON *item);
void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);

作用:将每一项的内容附加到指定的数组/对象
返回值:无
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(void);
extern cJSON *cJSON_CreateTrue(void);
extern cJSON *cJSON_CreateFalse(void);
extern cJSON *cJSON_CreateBool(int b);
extern cJSON *cJSON_CreateNumber(double num);
extern cJSON *cJSON_CreateString(const char *string);
cJSON_GetArraySize(const cJSON *array);
//Returns the number of items in an array (or object)
cJSON *cJSON_GetArrayItem(const cJSON *array, int item);
//Retrieve item number "item" from array "array". Returns NULL if unsuccessful
2、cjson打包数据

上面一些都是在创建或解析json文件所要用到的放法,我们先来创建一个比较简单的例子。json的数据如下

{
	"name": "taylor swift""age": 31,
	"album": "Lover",
	"song": "Miss Americana & The Heartbreak Prince"
}
#include <stdio.h>
#include "cJSON.h"

int main()
{
    cJSON * root =  cJSON_CreateObject();

    cJSON_AddItemToObject(root, "name", cJSON_CreateString("Taylor Swift"));//根节点下添加
    cJSON_AddItemToObject(root, "age", cJSON_CreateNumber(31));
    cJSON_AddItemToObject(root, "album", cJSON_CreateString("Lover"));
    cJSON_AddItemToObject(root, "song", cJSON_CreateString("Miss Americana & The Heartbreak Prince"));

    printf("%s\n", cJSON_Print(root));

    return 0;
}

上述json的格式所有的键对值都是在根节点下的,所以我们使用cJSON_AddItemToObject函数的第一个参数为同一个节点,也就是根节点。
第二个参数就是没想数据的key的名称,第三个参数为key对应的值的类型,不同类型用不同的函数。
gcc cJSON.c cjson_study_1.c -lm
在这里插入图片描述
上面简单的我们已经创建完毕,这时我们可以挑战一下更高难度的,有数组有嵌套数据,实际的编码和上面的差不多,只不过多了个嵌套和数组,数组的话我们就可以用新的函数来实现,下面为一个json对象实例

{
	"name": "Taylor Swfit",
	"age": 31,
	"album":{
			"1989":"Style""Reputation":[
							{
								"song":"Ready For It"
							},
							{
								"song":"End Game"
							}
						],
			"Lover":"Miss Americana & The Heartbreak Prince"
			},
	"address":"Tennessee"
}

下面则是代码,具体的思路如下,我们可以首先观察数据有几层(数组也可以算一层)格式,成对的{}出现(数组[]里的{}不算)和[]成对的出现,连个成对数目的相加就表示需要几个cJSON_CreateObject()所返回的指针,每个数组([]成对的)又需要一个额外的cJSON_CreateObject()返回的指针。具体的实现可以参考我下面的代码。
然后我们可以首先从最里面的一层也就是数组开始,然后一层一层的往外推,每层的添加顺序都是从第一个开始的,具体的可以参考下代码,水平有限很多东西还无法用文字表达

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

int main()
{
    cJSON * root =  cJSON_CreateObject();//生成的根节点
    cJSON * album =  cJSON_CreateObject();//album里嵌套的节点
    cJSON * rep_itm =  cJSON_CreateObject();//数组的里面的起始节点
    cJSON * rep_arr = cJSON_CreateArray();//生成一个空数组

    cJSON_AddItemToObject(rep_itm, "song", cJSON_CreateString("Ready For It"));//数组的第一个元素
    cJSON_AddItemToObject(rep_itm, "song", cJSON_CreateString("End Game"));//数组的第二个元素
    cJSON_AddItemToArray(rep_arr, rep_itm);//将数组的内容全部添加到这个数组的起始节点
    cJSON_AddItemToObject(album, "1989", cJSON_CreateString("Style"));//album节点下的第一个键值对
    //album节点下的第二个键值对,这是一个数组的信息,所以需要添加数组的信息
    cJSON_AddItemToObject(album, "Reputation", rep_arr);
    //albumn节点下的最后一个键值对,
    cJSON_AddItemToObject(album, "Lover", cJSON_CreateString("Miss Americana & The Heartbreak Prince"));

    cJSON_AddItemToObject(root, "name", cJSON_CreateString("Taylor Swift"));//根节点下添加第一个键值对
    cJSON_AddItemToObject(root, "age", cJSON_CreateNumber(31));//根节点下添加第二个键值对
    cJSON_AddItemToObject(root, "album", album);//节点下添加第三个键值对, album内嵌了一层,所以直接写它的指针
    cJSON_AddItemToObject(root, "address", cJSON_CreateString("Tennessee"));//跟节点下添加第四个键值对

    printf("%s\n", cJSON_Print(root));

    return 0;
}

gcc hhhhh.c cJSON.c -lm
在这里插入图片描述

3、cjson解析数据

上面我们学会了利用cjson库来生成json对象,那么竟然有生成那就肯定有打包,我们先看第一个简单的数据格式,也就是打包的第一个例子。我们首先把上面打包的数据输入到一个文件中,也可以选择直接写进字符串。

{
	"name": "taylor swift""age": 31,
	"album": "Lover",
	"song": "Miss Americana & The Heartbreak Prince"
}

在这里插入图片描述
查看文章的内容,结果是正确的,现在我们可以开始编程了。
在这里插入图片描述
源代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "cJSON.h"

#define FILENAME     "debug.txt"
#define SIZE         1024
int main(int argc, char *argv[])
{
    int      fd = -1;
    int      rv = -1;
    char     buf[SIZE];
    cJSON*   cjson = NULL;
    char*    string1 = NULL;
    char*    string2 = NULL;
    char*    string3 = NULL;
    int      age = 0;

    fd=open(FILENAME, O_RDWR);
    if(fd < 0)
    {
        printf("open %s failure\n", FILENAME);
        return -1;
    }

    rv=lseek(fd, 0, SEEK_CUR);
    if(rv < 0)
    {
        printf("lseek %s failure\n", FILENAME);
        return -1;
    }

    rv=read(fd, buf, sizeof(buf));
    if(rv < 0)
    {
        printf("read %s failure\n", FILENAME);
        return -1;
    }
    close(fd);

    cjson=cJSON_Parse(buf);//将数据转换成cjson格式,记得使用cJSON_Delete(cJSON *c), 防止内存泄漏
    if(cjson == NULL)//判断是否成功
    {
        printf("data pack to cjson failure\n");
        return -1;
    }
    printf("%s\r\n",cJSON_Print(cjson));//打印输出cjsong格式

    //cJSON_GetObjectItem(cjson,"cmd")返回的是一个结构体指针,所以我们可以通过指针知道对应的值
    string1=cJSON_GetObjectItem(cjson,"name")->valuestring;
    age=cJSON_GetObjectItem(cjson,"age")->valueint;
    string2=cJSON_GetObjectItem(cjson,"album")->valuestring;
    string3=cJSON_GetObjectItem(cjson,"song")->valuestring;

    printf("name:%s\r\n", string1);
    printf("age:%d\r\n", age);
    printf("album:%s\r\n", string2);
    printf("song:%s\r\n", string3);

    cJSON_Delete(cjson);//释放内存, 一般都是在最后释放,一定要等上面的打印完,因为这些指针指向这些内存


    return 0;
}

在这里插入图片描述
上面就是一些简单的json格式的解析,那么如果我们遇到了比较复杂的json数据,就像我们打包的第二个数据,里面有嵌套还有数组,这种复杂的数据我们该怎么解析呢。

先将json数据写入一个文本,数据如下
在这里插入图片描述
在这里插入图片描述

我们的思路是,首先找到根节点下有几个键值对,然后遍历这些键值对,每个键值对判断它是否是属于json对象,也就是是否有嵌套,有的话递归,没有的话就打印出来

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "cJSON.h"

#define FILENAME     "debug_2.txt"
#define SIZE         1024

void printJson(cJSON * root)//以递归的方式打印json的最内层键值对
{
    for(int i=0; i<cJSON_GetArraySize(root); i++)   //遍历最外层json键值对
    {
        cJSON * item = cJSON_GetArrayItem(root, i);
        if(cJSON_Object == item->type)      //如果对应键的值仍为cJSON_Object就递归调用printJson
            printJson(item);
        else                                //值不为json对象就直接打印出键和值
        {
            printf("%s:", item->string);
            printf("%s\r\n", cJSON_Print(item));
        }
    }

}
int main(int argc, char *argv[])
{
    int      fd = -1;
    int      rv = -1;
    char     buf[SIZE];
    cJSON*   cjson = NULL;
    char*    string1 = NULL;
    char*    string2 = NULL;
    char*    string3 = NULL;
    int      age = 0;

    fd=open(FILENAME, O_RDWR);
    if(fd < 0)
    {
        printf("open %s failure\n", FILENAME);
        return -1;
    }

    rv=lseek(fd, 0, SEEK_CUR);
    if(rv < 0)
    {
        printf("lseek %s failure\n", FILENAME);
        return -1;
    }

    rv=read(fd, buf, sizeof(buf));
    if(rv < 0)
    {
        printf("read %s failure\n", FILENAME);
        return -1;
    }
    close(fd);

	cjson=cJSON_Parse(buf);//将数据转换成cjson格式,记得使用cJSON_Delete(cJSON *c), 防止内存泄漏
    if(cjson == NULL)//判断是否成功
    {
        printf("data pack to cjson failure\n");
        return -1;
    }
    printf("%s\r\n",cJSON_Print(cjson));//打印输出cjsong格式

    printJson(cjson);
    cJSON_Delete(cjson);//释放内存, 一般都是在最后释放,一定要等上面的打印完,因为这些指针指向这些内存

    return 0;
}

在这里插入图片描述
运用上面第一个函数我们可以解决json数据解析的格式,当然这个函数适用于复杂的简单的所有json数据格式都能够解析。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值