cJSON详解

cJSON详解

笔者在以前的博客中介绍了C语言的简单数据解析,此方法适用于一些简单封装的数据,如坐标信息,物体抽象信息等。并且解析后的数据结构也比较单一,只有字符型和整型基本数据类型。而在一些大型嵌入式工程中,单位传输数据的体量和数据类型的复杂度都是非常庞大的。这时,我们需要引入JSON这一适用广泛的数据格式。

JSON数据格式简介

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,不是一种编程语言。JSON采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成,并有效地提升传输效率。

JSON语法

JSON的基本数据格式为键值对,中间由:隔开(牢记这一点),也可以理解为名称,本身必须是字符串可以是基本数据类型:字符串string数值number布尔值truefalsenull。高级数据类型:数组array使用[]表示和对象object使用{}表示。

JSON语法有如下特点

  • 数据在键值对中

  • 数据由逗号分隔

  • 方括号保存数组

  • 花括号保存对象

{
    "students" : [ {
            "name" : "XiaoMing",
            "age" : 10,
            "learning" : true
        }, {
            "name" : "XiaoHong",
            "age" : 11,
            "learning" : false
        } ]
}
  1. 该JSON语句最外层有一组花括号,故为一个对象object。该对象可以配合上层的组成键值对,也可以插入到上层的数组中,组成更加复杂的JSON数据。
  2. 该对象里有一个键值对键(名称)students数组array类型。该数组含有两个**对象object**类型的元素,使用,分隔。
  3. 每个对象类型的数组元素里有三个键值对,类型分别为字符串string数值number布尔值truefalse类型。在第一个元素中字符串string类型的name,其XiaoMing字符串,以此类推。
  4. 通过遍历该JSON语句,可以获得任意键(名称)所对应的值(数据)

cJSON概述

在JavaScript中,内置了JSON的解析器,可以直接使用JSON。但在c语言中并没有内置JSON相关的库,我们可以根据JSON语法自己写一个JSON解析器。当然也可以引用外部的JSON库来进行解析操作,这些库称为cJSON库

cJSON库

开源的cJSON库很多,笔者使用的cJSON库地址

打开下载后的文件夹,在此目录下可以看到cJSON.ccJSON.h文件。这两个文件是cJSON库的源码文件和头文件,在工程中包含这两个文件就可以使用该cJSON库。

使用方法

具体的使用方法在README.md文档中详细介绍,英文好的同学可自行学习,本节简要讲解cJSON库常用的两种使用方法。

直接使用源码文件

以cJSON库目录下测试文件test.c为例。

使用如下命令即可编译,生成的文件可直接运行。

gcc test.c cJSON.c cJSON.h -o test.exe	# Windows环境
gcc test.c cJSON.c cJSON.h -o test		# Linux环境

cJSON.ccJSON.h文件直接复制到工程,这种方法最为便捷。

安装至系统目录下(Linux环境)

在cJSON库目录下,执行如下命令:

make
sudo make install

系统会将头文件安装至/usr/local/include/cjson目录下,库文件安装至/usr/local/lib目录下。

在使用时#include <cjson/cJSON.h>包含头文件,并在编译时-lcjson链接库。

以cJSON库目录下测试文件test.c为例。

  • 将test.c文件中#include "cJSON.h"替换为#include <cjson/cJSON.h>

  • 使用gcc test.c -o test -lcjson编译,生成的文件可直接运行。

cJSON结构体及宏定义

本节主要讲解cJSON.h中定义的结构体和宏定义,对于一些cJSON内部使用的结构体和宏定义不做深入讲解。

cJSON结构体

cJSON最为核心的就是cJSON结构体:

/* The cJSON structure: */
typedef struct cJSON
{
    struct cJSON *next;			//后置节点指针
    struct cJSON *prev;			//前置节点指针
    struct cJSON *child;		//子节点指针
    
    int type;					//值 的类型
    char *valuestring;			//字符串数据
    int valueint;				//整型数据
    double valuedouble;			//浮点型数据
    
    char *string;				//名 的名称
} cJSON;

可见, cJOSN的核心采用了双向链表,通过*next*prev寻找目标节点,实现添加和删除操作。而且加入了*child子节点指针,可以实现更加复杂的层级结构。

type代表的类型,其取值在如下结构体中定义:

/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False  (1 << 0)
#define cJSON_True   (1 << 1)
#define cJSON_NULL   (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array  (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw    (1 << 7) /* raw json */

通过读取type的值,可以判断该结构体内哪种类型的值有效,进而操作对应的数据。

cJSON_Hooks结构体

cJSON支持自定义内存管理函数,其结构体定义如下所示:

typedef struct cJSON_Hooks
{
      void *(CJSON_CDECL *malloc_fn)(size_t sz);
      void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;

此结构体内有两个函数指针,通过名字我们可以知道这就是内存申请和释放的函数指针。通过编写自定义的内存管理函数对该结构体赋值,调用cJSON_InitHooks函数对global_hooks全局变量赋值,该全局变量类型及初始化如下:

typedef struct internal_hooks
{
    void *(CJSON_CDECL *allocate)(size_t size);
    void (CJSON_CDECL *deallocate)(void *pointer);
    void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
} internal_hooks;

static internal_hooks global_hooks = {
    internal_malloc,
    internal_free,
    internal_realloc
};

internal_hooks结构体成员也是内存管理的函数指针,不同于cJSON_InitHooks,它多一个reallocate成员。在global_hooks全局变量中,其成员通过宏定义全部初始化为默认的内存管理函数,即mallocfreereallocinternal_realloc无法通过cJSON_InitHooks函数重新定义。

接下来cJSON库会调用global_hooks全局变量里面的内存管理函数。

例:在cJSON_ParseWithLengthOpts函数中,使用item = cJSON_New_Item(&global_hooks);语句创建一个节点指针,cJSON_New_Item原型如下:

/* Internal constructor. */
static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
{
    cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
    if (node) {
        memset(node, '\0', sizeof(cJSON));
    }
    return node;
}

由此可知,cJSON_New_Item通过调用global_hooks中的allocate成员申请内存。

其他宏定义

版本信息

/* project version */
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 14

调用cJSON_Version函数可打印此信息,有些cJSON库可能不兼容过低版本的库,会导致解析失败。

嵌套深度

#define CJSON_NESTING_LIMIT 1000

为了防止堆栈溢出做出的保护措施,在一些小型嵌入式系统中可以给改此值。

cJSON应用函数

本节主要讲解cJSON.h中定义的函数。

cJSON主要函数

cJSON_Parse

CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);

将cJSON字符串格式化为cJSON链表,并返回指向此链表的指针。

cJSON_Delete

CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);

释放位于堆中的cJSON结构体内存。使用cJSON_ParseCreate类的函数初始化的cJSON结构体,在不使用时须调用cJSON_Delete进行释放,否则会导致内存泄漏。形成链表关系时,调用此函数释放根节点时会释放整条链表,注意不要double free

cJSON_Print

CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);

将cJSON链表解析成带格式的JSON字符串,并返回指向位于中的该字符串的指针。不使用时须调用free释放该内存。

cJSON组包函数

创建指定类型的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);
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
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);
添加JSON对象到JSON链表中
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
创建JSON对象并添加到JSON链表中
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);

cJSON解包函数

获取JSON对象
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
获取JSON数据
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);

判断cJSON对象数据类型

CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);

从cJSON链表中删除一个cJSON数据

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);

cJSON应用示例

cJSON组包操作

#include <stdio.h>
#include <cjson/cJSON.h>

int main(int argc, char **argv)
{
    cJSON *root = cJSON_CreateObject();
    cJSON *students = cJSON_CreateArray();
    cJSON *students1 = cJSON_CreateObject();
    cJSON *students2 = cJSON_CreateObject();

    students = cJSON_AddArrayToObject(root, "students");

    cJSON_AddItemToArray(students, students1);
    cJSON_AddItemToArray(students, students2);

    cJSON_AddStringToObject(students1, "name", "XiaoMing");
    cJSON_AddNumberToObject(students1, "age", 10);
    cJSON_AddTrueToObject(students1, "learning");

    cJSON_AddStringToObject(students2, "name", "XiaoHong");
    cJSON_AddNumberToObject(students2, "age", 11);
    cJSON_AddFalseToObject(students2, "learning");

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

    cJSON_Delete(root);
    return 0;
}

执行结果

{
        "students":     [{
                        "name": "XiaoMing",
                        "age":  10,
                        "learning":     true
                }, {
                        "name": "XiaoHong",
                        "age":  11,
                        "learning":     false
                }]
}

cJSON解包操作

#include <stdio.h>
#include <cjson/cJSON.h>

int main(int argc, char **argv)
{
    const char *str = "{\"students\":[{\"name\" : \"XiaoMing\", \"age\" : 10, \"learning\" : true},{\"name\" : \"XiaoHong\", \"age\" : 11, \"learning\" : false}]}";
    cJSON *root = NULL;
    cJSON *students = NULL, *students1 = NULL, *students2 = NULL;
    cJSON *name1 = NULL, *age1 = NULL, *learning1 = NULL;
    cJSON *name2 = NULL, *age2 = NULL, *learning2 = NULL;

    root = cJSON_Parse(str);

    if (root == NULL){   
        printf("cJSON ERROR : %s\n", cJSON_GetErrorPtr());
    } 
    students = cJSON_GetObjectItem(root, "students");

    students1 = cJSON_GetArrayItem(students, 0);
    students2 = cJSON_GetArrayItem(students, 1);

    name1 = cJSON_GetObjectItem(students1, "name");
    age1 = cJSON_GetObjectItem(students1, "age");
    learning1 = cJSON_GetObjectItem(students1, "learning");

    name2 = cJSON_GetObjectItem(students2, "name");
    age2 = cJSON_GetObjectItem(students2, "age");
    learning2 = cJSON_GetObjectItem(students2, "learning");

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

    printf("name        --> %s\n", cJSON_GetStringValue(name1));
    printf("age         --> %0.f\n", cJSON_GetNumberValue(age1));
    printf("learning    --> %d\n", cJSON_IsTrue(learning1));

    printf("name        --> %s\n", cJSON_GetStringValue(name2));
    printf("age         --> %0.f\n", cJSON_GetNumberValue(age2));
    printf("learning    --> %d\n", cJSON_IsTrue(learning2));

    cJSON_Delete(root);
    return 0;
}

执行结果

{
        "students":     [{
                        "name": "XiaoMing",
                        "age":  10,
                        "learning":     true
                }, {
                        "name": "XiaoHong",
                        "age":  11,
                        "learning":     false
                }]
}
name        --> XiaoMing
age         --> 10
learning    --> 1
name        --> XiaoHong
age         --> 11
learning    --> 0
  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
cJSON是一个用于在C语言中操作JSON数据的开发库。您可以通过在Linux系统上编译和安装cJSON库来使用它。以下是使用cJSON库创建和操作JSON数据的示例代码: ```c #include <stdio.h> #include "cJSON.h" int main() { cJSON *root = cJSON_CreateObject(); // 创建根节点 cJSON *item = cJSON_CreateObject(); // 创建semantic节点 cJSON *next = cJSON_CreateObject(); // 创建slots节点 cJSON_AddItemToObject(root, "rc", cJSON_CreateNumber(0)); // 在根节点下添加rc节点 cJSON_AddItemToObject(root, "operation", cJSON_CreateString("CALL")); // 在根节点下添加operation节点 cJSON_AddItemToObject(root, "service", cJSON_CreateString("telephone")); // 在根节点下添加service节点 cJSON_AddItemToObject(root, "text", cJSON_CreateString("打电话给张三")); // 在根节点下添加text节点 cJSON_AddItemToObject(root, "semantic", item); // 在根节点下添加semantic节点 cJSON_AddItemToObject(item, "slots", next); // 在semantic节点下添加slots节点 cJSON_AddItemToObject(next, "name", cJSON_CreateString("张三")); // 在slots节点下添加name节点 printf("%s\n", cJSON_Print(root)); // 打印整个JSON字符串 return 0; } ``` 这段代码将创建一个包含您提供的样例JSON数据的JSON对象,并打印出整个JSON字符串。您可以根据您的需求修改和扩展这段代码来操作JSON数据。 请注意,您需要在编译和链接过程中将cJSON库链接到您的项目中。您可以通过克隆cJSON源码并将其添加到您的项目中来完成这一步骤。有关如何在Linux上使用cJSON库的更多信息,请参考cJSON库的文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值