千家信息网

JSON组件使用方法是什么

发表于:2025-01-28 作者:千家信息网编辑
千家信息网最后更新 2025年01月28日,本篇内容主要讲解"JSON组件使用方法是什么",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"JSON组件使用方法是什么"吧!1. JSON与cJSONJSO
千家信息网最后更新 2025年01月28日JSON组件使用方法是什么

本篇内容主要讲解"JSON组件使用方法是什么",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"JSON组件使用方法是什么"吧!

1. JSON与cJSON

JSON -- 轻量级的数据格式

JSON 全称 JavaScript Object Notation,即 JS对象简谱,是一种轻量级的数据格式。

它采用完全独立于编程语言的文本格式来存储和表示数据,语法简洁、层次结构清晰,易于人阅读和编写,同时也易于机器解析和生成,有效的提升了网络传输效率。

JSON语法规则

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

  • 以"{"开始,以"}"结束,允许嵌套使用;

  • 每个名称和值成对出现,名称和值之间使用":"分隔;

  • 键值对之间用","分隔

  • 在这些字符前后允许存在无意义的空白符;

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

  • 一个新的json对象

  • 数组:使用"["和"]"表示

  • 数字:直接表示,可以是整数,也可以是浮点数

  • 字符串:使用引号"表示

  • 字面值:false、null、true中的一个(必须是小写)

示例如下:

{    "name": "mculover666",    "age": 22,    "weight": 55.5    "address":    {        "country": "China",        "zip-code": 111111    },    "skill": ["c", "Java", "Python"],    "student": false}

LiteOS中的cJSON组件

cJSON是一个使用C语言编写的JSON数据解析器,具有超轻便,可移植,单文件的特点,使用MIT开源协议。

LiteOS中已经移植了cJSON,作为一个组件使用,源码在sdk\IoT_LINK_1.0.0\iot_link\cJSON中,其源码文件只有两个:

  • cJSON.h

  • cJSON.c

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

#include "cJSON.h"

2. cJSON数据结构和设计思想

cJSON的设计思想从其数据结构上就能反映出来。

cJSON使用cJSON结构体来表示一个JSON数据,定义在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;

cJSON的设计很巧妙。

首先,它不是将一整段JSON数据抽象出来,而是将其中的一条JSON数据抽象出来,也就是一个键值对,用上面的结构体 strcut cJSON 来表示,其中用来存放值的成员列表如下:

  • String:用于表示该键值对的名称;

  • type:用于表示该键值对中值的类型;

  • valuestring:如果键值类型(type)是字符串,则将该指针指向键值;

  • valueint:如果键值类型(type)是整数,则将该指针指向键值;

  • valuedouble:如果键值类型(type)是浮点数,则将该指针指向键值;

其次,一段完整的JSON数据中由很多键值对组成,并且涉及到键值对的查找、删除、添加,所以使用链表来存储整段JSON数据,如上面的代码所示:

  • next指针:指向下一个键值对

  • prev指针指向上一个键值对

最后,因为JSON数据支持嵌套,所以一个键值对的值会是一个新的JSON数据对象(一条新的链表),也有可能是一个数组,方便起见,在cJSON中,数组也表示为一个数组对象,用链表存储,所以:

在键值对结构体中,当该键值对的值是一个嵌套的JSON数据或者一个数组时,由child指针指向该条新链表。

3. 开启cJSON组件

在LiteOS中,cJSON组件默认是未开启的,使用宏定义CONFIG_JSON_ENABLE开启。

开启之后,LiteOS会自动进行初始化,并且使用cJSON的内存钩子将cJSON申请内存的方式变为使用osal_malloc申请,自动初始化代码在link_main.c文件中:

4. JSON数据封装

封装方法

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

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

  • 头指针:指向链表头结点的指针;

  • 头结点:不存放有效数据,方便链表操作;

  • 首节点:第一个存放有效数据的节点;

  • 尾节点:最后一个存放有效数据的节点;

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

  • ① 创建头指针:

 cJSON* cjson_test = NULL;
  • ② 创建头结点,并将头指针指向头结点:

cjson_test = cJSON_CreateObject();
  • ③ 尽情的向链表中添加节点:

cJSON_AddNullToObject(cJSON * const object, const char * const name);cJSON_AddTrueToObject(cJSON * const object, const char * const name);cJSON_AddFalseToObject(cJSON * const object, const char * const name);cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);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数据

上面讲述,一段完整的JSON数据就是一条长长的链表,那么,如何打印出这段JSON数据呢?

cJSON提供了一个API,可以将整条链表中存放的JSON信息输出到一个字符串中:

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

使用的时候,只需要接收该函数返回的指针地址即可。

封装数据和打印数据示例

单纯的讲述方法还不够,下面用一个例子来说明,封装出开头给出的那段JSON数据。

首先基于HelloWorld工程,创建一个存放示例文件的文件夹cloud_test_demo,并新建一个实验文件cjson_print_demo.c,编写如下代码:

#include #include #include static int cjson_print_demo_entry(){    cJSON* cjson_test = NULL;    cJSON* cjson_address = NULL;    cJSON* cjson_skill = NULL;    char* str = NULL;    /* 创建一个JSON数据对象(链表头结点) */    cjson_test = cJSON_CreateObject();    /* 添加一条字符串类型的JSON数据(添加一个链表节点) */    cJSON_AddStringToObject(cjson_test, "name", "mculover666");    /* 添加一条整数类型的JSON数据(添加一个链表节点) */    cJSON_AddNumberToObject(cjson_test, "age", 22);    /* 添加一条浮点类型的JSON数据(添加一个链表节点) */    cJSON_AddNumberToObject(cjson_test, "weight", 55.5);    /* 添加一个嵌套的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);    /* 添加一个数组类型的JSON数据(添加一个链表节点) */    cjson_skill = cJSON_CreateArray();    cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));    cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java" ));    cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));    cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);    /* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */    cJSON_AddFalseToObject(cjson_test, "student");    /* 打印JSON对象(整条链表)的所有数据 */    str = cJSON_Print(cjson_test);    printf("%s\n", str);    /* 释放整条链表内存 */    cJSON_Delete(cjson_test);    return 0;}int standard_app_demo_main(){    osal_task_create("cjson_print_demo",cjson_print_demo_entry,NULL,0x800,NULL,2);    return 0;}

在user_demo.mk中配置文件路径:

        #example for cjson_print_demo        ifeq ($(CONFIG_USER_DEMO), "cjson_print_demo")                      user_demo_src  = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/cloud_test_demo/cjson_print_demo.c}        endif

位置如下:

然后在.sdkconfig中开启cJSON组件,并且选中该demo:

实验结果如图:

5. cJSON数据解析

解析方法

解析JSON数据的过程,其实就是剥离一个一个链表节点(键值对)的过程。

解析方法如下:

  • ① 创建链表头指针:

cJSON* cjson_test = NULL;
  • ② 解析整段JSON数据,并将链表头结点地址返回,赋值给头指针:

解析整段数据使用的API只有一个:

(cJSON *) cJSON_Parse(const char *value);
  • ③ 根据键值对的名称从链表中取出对应的值,返回该键值对(链表节点)的地址

(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
  • ④ 如果JSON数据的值是数组,使用下面的两个API提取数据:

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

解析示例

下面用一个例子来说明如何解析出开头给出的那段JSON数据。

在存放示例文件的文件夹cloud_test_demo中,再新建一个实验文件cjson_parse_demo.c,编写如下代码:

#include #include #include char *message = "{                              \    \"name\":\"mculover666\",   \    \"age\": 22,                \    \"weight\": 55.5,           \    \"address\":                \        {                       \            \"country\": \"China\",\            \"zip-code\": 111111\        },  \    \"skill\": [\"c\", \"Java\", \"Python\"],\    \"student\": false\}";static int cjson_test1_demo_entry(){   cJSON* cjson_test = NULL;    cJSON* cjson_name = NULL;    cJSON* cjson_age = NULL;    cJSON* cjson_weight = NULL;    cJSON* cjson_address = NULL;    cJSON* cjson_address_country = NULL;    cJSON* cjson_address_zipcode = NULL;    cJSON* cjson_skill = NULL;    cJSON* cjson_student = NULL;    int    skill_array_size = 0, i = 0;    cJSON* cjson_skill_item = NULL;    /* 解析整段JSO数据 */    cjson_test = cJSON_Parse(message);    if(cjson_test == NULL)    {        printf("parse fail.\n");        return -1;    }    /* 依次根据名称提取JSON数据(键值对) */    cjson_name = cJSON_GetObjectItem(cjson_test, "name");    cjson_age = cJSON_GetObjectItem(cjson_test, "age");    cjson_weight = cJSON_GetObjectItem(cjson_test, "weight");    printf("name: %s\n", cjson_name->valuestring);    printf("age:%d\n", cjson_age->valueint);    printf("weight:%.1f\n", cjson_weight->valuedouble);    /* 解析嵌套json数据 */    cjson_address = cJSON_GetObjectItem(cjson_test, "address");    cjson_address_country = cJSON_GetObjectItem(cjson_address, "country");    cjson_address_zipcode = cJSON_GetObjectItem(cjson_address, "zip-code");    printf("address-country:%s\naddress-zipcode:%d\n", cjson_address_country->valuestring, cjson_address_zipcode->valueint);    /* 解析数组 */    cjson_skill = cJSON_GetObjectItem(cjson_test, "skill");    skill_array_size = cJSON_GetArraySize(cjson_skill);    printf("skill:[");    for(i = 0; i < skill_array_size; i++)    {        cjson_skill_item = cJSON_GetArrayItem(cjson_skill, i);        printf("%s,", cjson_skill_item->valuestring);    }    printf("\b]\n");    /* 解析布尔型数据 */    cjson_student = cJSON_GetObjectItem(cjson_test, "student");    if(cjson_student->valueint == 0)    {        printf("student: false\n");    }    else    {        printf("student:error\n");    }    /* 释放整条链表内存 */    cJSON_Delete(cjson_test);        return 0;}int standard_app_demo_main(){    osal_task_create("cjson_test1_demo",cjson_test1_demo_entry,NULL,0x800,NULL,2);    return 0;}

在user_demo.mk中配置文件路径:

        #example for cjson_parse_demo        ifeq ($(CONFIG_USER_DEMO), "cjson_parse_demo")                      user_demo_src  = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/cloud_test_demo/cjson_parse_demo.c}        endif

位置如下:

然后在.sdkconfig中开启cJSON组件,并且选中该demo:

实验结果如图:

注意事项

在本示例中,因为我提前知道数据的类型,比如字符型或者浮点型,所以我直接使用指针指向对应的数据域提取,在实际使用时,如果提前不确定数据类型,应该先判断type的值,确定数据类型,再从对应的数据域中提取数据

6. cJSON使用过程中的内存问题

内存及时释放

cJSON的所有操作都是基于链表的,所以cJSON在使用过程中大量的使用malloc从堆中分配动态内存的,所以在使用完之后,应当及时调用下面的函数,清空cJSON指针所指向的内存,该函数也可用于删除某一条数据:

(void) cJSON_Delete(cJSON *item);

注意:该函数删除一条JSON数据时,如果有嵌套,会连带删除。

内存钩子

cJSON在支持自定义malloc函数和free函数,方法如下:

  • ① 使用cJSON_Hooks来连接自定义malloc函数和free函数:

typedef struct cJSON_Hooks{      /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */      void *(CJSON_CDECL *malloc_fn)(size_t sz);      void (CJSON_CDECL *free_fn)(void *ptr);} cJSON_Hooks;
  • ② 初始化钩子cJSON_Hooks

(void) cJSON_InitHooks(cJSON_Hooks* hooks);

到此,相信大家对"JSON组件使用方法是什么"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

数据 指针 节点 文件 类型 方法 组件 内存 指向 函数 数组 对象 名称 字符 示例 结构 结点 过程 封装 有效 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 e版管理端添加不了服务器 校园网络安全宣传语作文 学计算机网络安全的学生费用高吗 网络技术公司技术员工作 数据库 学哪个版本的 合并两个数据库重复数据 计算机网络技术专业属于什么科 天津网络安全公安总队 软件开发面试笔试题及答案 java中存取数据库的包 芜湖网新网络技术有限公司泡澡桶 数据库的邮编 网络安全进校园简讯 软件开发专业枯不枯燥 宝山区正规软件开发包括什么 bgp高防服务器租用计费 软件开发论文怎么题目 我们应该如何学好网络安全专业 维护网络安全工作简报 夸克浏览器代理服务器 海康威视服务器进阵列卡 mysql 数据库导不出 服务器登录不默认管理员账户 县委办网络安全管理制度 信息网络安全宣传ppt 插入数据库耗时解决方案 vpn 虚拟服务器 软件开发越复杂越难维护 山东省图书馆数据库登录 生物传感技术与数据库
0