cJSON的使用和注意事项

目录

1.cJSON源码获取

2.cJSON的使用

2.1 增

2.2 删

2.3 改

2.4 查

3.使用注意事项

4.cJSON源码简要分析


json格式的数据应用十分广泛,作为嵌入式开发,用C/C++比较多,这里介绍一个开源处理json的库:cJSON,这个库用起来很简单,但是也有些地方需要注意。这里来记录下。

注意:不同版本的cJSON是有差别的,不过都大同小异,本文使用的cJSON版本是1.7.15。

1.cJSON源码获取

cJSON库获取

2.cJSON的使用

将上面所有文件下载下来后,这里只需要关注两个文件:cJSON.c和cJSON.h

下面就来看看这两个文件中的代码如何使用:

2.1 增

相关API:

/* Helper functions for creating and adding items to an object at the same time.
 * They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);

举例:

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

int main(void)
{
    char jsonBuf[] = "{\"name\":\"lhsmd\", \"sex\":\"男\", \"fav\":{\"eat\":\"water\"}}";
    cJSON *cJSONTest = cJSON_Parse(jsonBuf);   /* 记得调用cJSON_Delete释放空间 */
    cJSON *p = cJSON_GetObjectItem(cJSONTest, "name");
    if(cJSON_IsString(p))                   /* 判断key的value是否为string */
    {
        printf("%s\r\n", p->valuestring);   /* 输出 */
    }



    cJSON *p1 = cJSON_AddStringToObject(cJSONTest, "add", "me");
    if(p1 != NULL)
    {
        char *p2 = cJSON_Print(cJSONTest);  /* 按照格式输出json  cJSON_PrintUnformatted表示格式化输出输出是一长串 */
        if(p2 != NULL)
        {
            printf("%s\r\n", p2);
            free(p2);
            p2 = NULL;
        }
    }

    cJSON_Delete(cJSONTest);
    return 0;
}


输出结果:

2.2 删

相关API:

/* Remove/Detach items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);

举例:

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

int main(void)
{
    char jsonBuf[] = "{\"name\":\"lhsmd\", \"sex\":\"男\", \"fav\":{\"eat\":\"water\"}}";
    cJSON *cJSONTest = cJSON_Parse(jsonBuf);   /* 记得调用cJSON_Delete释放空间 */
    cJSON *p = cJSON_GetObjectItem(cJSONTest, "name");
    if(cJSON_IsString(p))                   /* 判断key的value是否为string */
    {
        printf("%s\r\n", p->valuestring);   /* 输出 */
    }

    cJSON_DeleteItemFromObject(cJSONTest, "name");
    char *p3 = cJSON_Print(cJSONTest);  /* 按照格式输出json  cJSON_PrintUnformatted表示格式化输出输出是一长串 */
    if(p3 != NULL)
    {
        printf("%s\r\n", p3);
        free(p3);
        p3 = NULL;
    }
    cJSON_Delete(cJSONTest);
    return 0;
}


输出:

2.3 改

相关API:

/* Update array items. */
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);

举例:

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

int main(void)
{
    char jsonBuf[] = "{\"name\":\"lh\", \"sex\":\"男\", \"fav\":{\"eat\":\"water\"}}";
    cJSON *cJSONTest = cJSON_Parse(jsonBuf);   /* 记得调用cJSON_Delete释放空间 */
    cJSON *p = cJSON_GetObjectItem(cJSONTest, "name");
    if(cJSON_IsString(p))                   /* 判断key的value是否为string */
    {
        printf("%s\r\n", p->valuestring);   /* 输出 */
    }

    cJSON_ReplaceItemInObjectCaseSensitive(cJSONTest, "name", cJSON_CreateString("smd"));
    char *p2 = cJSON_Print(cJSONTest);  /* 按照格式输出json  cJSON_PrintUnformatted表示格式化输出输出是一长串 */
    if(p2 != NULL)
    {
        printf("%s\r\n", p2);
        free(p2);
        p2 = NULL;
    }

    cJSON *json = cJSON_CreateObject();
    if(json != NULL)
    {
        if(cJSON_AddStringToObject(json, "eat" ,"food"))
        {
            if(cJSON_ReplaceItemInObjectCaseSensitive(cJSONTest, "fav", json))
            {
                p2 = cJSON_Print(cJSONTest);  /* 按照格式输出json  cJSON_PrintUnformatted表示格式化输出输出是一长串 */
                if(p2 != NULL)
                {
                    printf("%s\r\n", p2);
                    free(p2);
                    p2 = NULL;
                }
            }
        }
    }
    cJSON_Delete(cJSONTest);
    return 0;
}

输出:

2.4 查

查在使用中是使用最多的了,这里给一个实际用到的例子:

下面一段json数据表示近三天的天气情况,现在需要用C语言将其中的日期、日期对应的天气情况、最高低气温、风向以及湿度等信息提取出来,json数据如下:

{
    "results": [
        {
            "location": {
                "id": "WT029G15ETRJ",
                "name": "长沙",
                "country": "CN",
                "path": "长沙,长沙,湖南,中国",
                "timezone": "Asia/Shanghai",
                "timezone_offset": "+08:00"
            },
            "daily": [
                {
                    "date": "2022-08-28",
                    "text_day": "阴",
                    "code_day": "9",
                    "text_night": "阴",
                    "code_night": "9",
                    "high": "37",
                    "low": "25",
                    "rainfall": "0.00",
                    "precip": "0.00",
                    "wind_direction": "西南",
                    "wind_direction_degree": "222",
                    "wind_speed": "5.36",
                    "wind_scale": "1",
                    "humidity": "83"
                },
                {
                    "date": "2022-08-29",
                    "text_day": "阴",
                    "code_day": "9",
                    "text_night": "阴",
                    "code_night": "9",
                    "high": "37",
                    "low": "24",
                    "rainfall": "0.00",
                    "precip": "0.00",
                    "wind_direction": "西南",
                    "wind_direction_degree": "244",
                    "wind_speed": "7.96",
                    "wind_scale": "2",
                    "humidity": "86"
                },
                {
                    "date": "2022-08-30",
                    "text_day": "阴",
                    "code_day": "9",
                    "text_night": "阴",
                    "code_night": "9",
                    "high": "32",
                    "low": "21",
                    "rainfall": "0.00",
                    "precip": "0.00",
                    "wind_direction": "北",
                    "wind_direction_degree": "341",
                    "wind_speed": "18.72",
                    "wind_scale": "3",
                    "humidity": "75"
                }
            ],
            "last_update": "2022-08-28T08:00:00+08:00"
        }
    ]
}

想要的数据信息在键“daily”下,显然,要获取“daily”的内容,需要一层一层“剥开”,这个相对简单,只需要“剥开”两层:“results”->“daily”。其C语言代码如下:

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

char* jsonFromFile(const char* filename)
{
    FILE* fp = fopen(filename, "r");
    if (!fp)
    {
        return NULL;
    }
    fseek(fp, 0, SEEK_END);  
    long len = ftell(fp);    /* 获取文件大小 */
    printf("len = %d\r\n", len);
    fseek(fp, 0, SEEK_SET);  /* 将文件读写指针再次定位到文件内容开头 */
    char* buf = (char *)malloc( sizeof(char)*len + 1);  /* 申请空间,记得释放 */
    memset(buf, 0, sizeof(buf));
    fread(buf, sizeof(char), len, fp);
    fclose(fp);
    return buf;
}

int main()
{ 
    char* jsondata = jsonFromFile("1.json");
    cJSON*root=cJSON_Parse(jsondata);
    free(jsondata);
    if(root != NULL)
    {
        printf("[INFO] Transfer OK!\r\n");
        cJSON *p = cJSON_GetObjectItem(root, "results");  //result下是一个数组,且只有一个元素
        if(p != NULL)
        {
            cJSON *results_json = cJSON_GetArrayItem(p, 0); //第0个元素
            if(results_json != NULL)
            {
                printf("[INFO] results_json cJSON_GetArrayItem ok\r\n");
                cJSON *daily_json = cJSON_GetObjectItem(results_json, "daily"); //获取“daily”的值
                if(daily_json != NULL)
                {
                    for(int i = 0;i < cJSON_GetArraySize(daily_json); i++)
                    {
                        printf("日期时间:%s\r\n", cJSON_GetObjectItem(cJSON_GetArrayItem(daily_json, i), "date")->valuestring);
                        printf("白天天气:%s\r\n", cJSON_GetObjectItem(cJSON_GetArrayItem(daily_json, i), "text_day")->valuestring);
                        printf("夜间天气:%s\r\n", cJSON_GetObjectItem(cJSON_GetArrayItem(daily_json, i), "text_night")->valuestring);
                        printf("最高气温:%s\r\n", cJSON_GetObjectItem(cJSON_GetArrayItem(daily_json, i), "high")->valuestring);
                        printf("最低气温:%s\r\n", cJSON_GetObjectItem(cJSON_GetArrayItem(daily_json, i), "low")->valuestring);
                        printf("风向:%s\r\n", cJSON_GetObjectItem(cJSON_GetArrayItem(daily_json, i), "wind_direction")->valuestring);
                        printf("湿度:%s\r\n\r\n", cJSON_GetObjectItem(cJSON_GetArrayItem(daily_json, i), "humidity")->valuestring);
                    }
                }
            }
        }
        cJSON_Delete(root);
    }
}

3.使用注意事项

在使用cJSON的过程,需要注意堆空间的释放,具体需要关注的对象有:

1.cJSON_Print系列,需要使用free或者cJSON_free释放

/* Render a cJSON entity to text for transfer/storage. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
/* Render a cJSON entity to text for transfer/storage without any formatting. */
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);

2.cJSON_Create系列,使用cJSON_Delete释放


/* These calls create a cJSON item of the appropriate type. */
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);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);

/* Create a string where valuestring references a string so
 * it will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
/* Create an object/array that only references it's elements so
 * they will not be freed by cJSON_Delete */
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);

/* These utilities create an Array of count items.
 * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);

3.cJSON_Parse,使用cJSON_Delete释放

CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);

cJSON_Delete和cJSON_Free(free)的区别:

cJSON_Delete会释放整个cJSON对象,而free只是释放某个节点的空间,在使用时只需要注意:变量类型为char*的使用free释放空间,变量类型为cJSON*的,使用cJSON_Delete释放空间。

4.cJSON源码简要分析

在cJSON源码的cJSON.h中,有一个很重要的结构体:

/* The cJSON structure: */
typedef struct cJSON
{
    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *next;
    struct cJSON *prev;
    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
    struct cJSON *child;

    /* The type of the item, as above. */
    int type;

    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
    char *valuestring;
    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
    int valueint;
    /* The item's number, if type==cJSON_Number */
    double valuedouble;

    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
    char *string;
} cJSON;

这是一个双向循环链表,事实上,从代码分析,在给定json的数据中,每一个键值对都是一个链表里面的一个节点。json数据的增删改查都是通过这个双向链表实现的。

如果有时间,在介绍学习具体的源码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

all of the time

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值