cJSON学习

article/2025/8/26 18:07:01

最近在做数据上三大云平台的项目,用的是MQTT协议,阿里云、腾讯云、华为云那边解析数据用的是json格式,下发的也是json格式,所以特地学一下json的封装和解析过程,以及其API的使用,作了以下笔记。

文章目录

  • 一、概述
  • 二、下载安装
  • 二、基本语法
    • (1)JSON表示方法
    • (2)JSON的三种语法
    • (3)JSON的值
    • (4)JSON两种结构
  • 三、cJSON数据结构
  • 四、cJSON封装过程
    • (1)创建头指针:
    • (2)创建头节点,并将头指针指向头节点:
    • (3)向链表添加节点:
    • (4)输出cJSON数据
    • (5)使用实例
  • 五、cJSON数据解析
    • (1)cJSON_Parse
    • (2)cJSON_GetObjectItem
    • (3)cJSON_Delete
    • 注意事项

一、概述

cJSON是一个仅有一个.h文件,一个.c文件组成的JSON解析器,它是由纯C(ANSI C89)实现的,跨平台性较好。cJSON是采用链表存储的。

cJSON库在使用的时候只需要如下两步:将cJSON.c(或者库文件)和cJSON.h添加到项目中即可;如果在命令行中进行链接还需要加上-lm表示链接math库。

二、下载安装

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

使用的时候找到cJSON.c和cJSON.h 文件将其添加到项目工程中,然后包含头文件cJSON.h即可。

二、基本语法

(1)JSON表示方法

以"{“开始,以”}"结束
数据在键/值对中;
数据由逗号分开;
花括号保存对象;
方括号保存数组;

(2)JSON的三种语法

键/值对key:value,中间用冒号分开,键要加双引号,但值不一定,例如"name":“zcx”。
对象放在花括号中,可用包含多个键值对,例如{“name”:“zcx”,“address”:“深圳”}。
数组JSON数组放在方括号中,数组成员可以是对象、值、也可以是数组,例如[“小明”,“二狗”,“翠花”,{“adress”:“北京”}]。

(3)JSON的值

可以是:数字(整数或浮点数)、字符串(在双引号中)、布尔值(true或false)、数组(在方括号中)、一个json对象(在花括号中)、null。

(4)JSON两种结构

**对象:**对象在js中表示为“{}”括起来的内容,数据结构为{key:value,key:value,…}的键值对的结构,在面向对象的语言中,key为对象的属性,value为对应的属性值,所以很容易理解,取值方法为 对象.key 获取属性值.

**数组:**数组在js中是中括号“[]”括起来的内容,数据结构为[“java”,“javascript”,“vb”,…],取值方式和所有语言中一样,使用索引获取。

经过对象、数组2种结构就可以组合成复杂的数据结构了。

三、cJSON数据结构

cJSON使用cJSON结构体来表示一个JSON数据,定义在cJSON.h中,源码如下:

/* The cJSON structure: */
typedef struct cJSON
{//next指针:指向下一个键值对struct cJSON *next;//prev指针指向上一个键值对struct cJSON *prev;//在键值对结构体中,当该键值对的值是一个嵌套的JSON数据或者一个数组时,由child指针指向该条新链表。struct cJSON *child;//type:用于表示该键值对中值的类型;int type;//valuestring:如果键值类型(type)是字符串,则将该指针指向键值;char *valuestring;//valueint:如果键值类型(type)是整数,则将该指针指向键值;int valueint;//valuedouble:如果键值类型(type)是浮点数,则将该指针指向键值;double valuedouble;//String:用于表示该键值对的名称;char *string;
} cJSON;

cJSON将其中一条json数据(键值对)抽象出来,用上面的结构体表示。但是往往一段完整的json数据不只一个数据,而且还需要进行增删改查等操作,所以其使用了链表的形式进行存放json数据,json中,数组也表示一个对象,用链表存储。

四、cJSON封装过程

封装JSON数据的过程,其实就是创建链表和向链表中添加节点的过程。熟悉一下单链表,以及一些术语:

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

在这里插入图片描述

不过cJSON用的可不是单链表存储数据,而是双亲孩子兄弟表示法,因为其一个节点数据结构里面包含了三个指针,分别是next、prev、child。

明白了这几个概念,我们就可以知道一个cJSON数据是如何创建的。

(1)创建头指针:

cJSON*	cjson_test = NULL;

(2)创建头节点,并将头指针指向头节点:

cjson_test = cJSON_CreateObject();

(3)向链表添加节点:


//创建一个数组对象,返回一个cJSON结构体类型的指针。
cJSON *cJSON_CreateArray();cJSON_AddNullToObject(cJSON * const object, const char * const name);//向对象添加true布尔值
cJSON_AddTrueToObject(cJSON * const object, const char * const name);//向对象添加false布尔值
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);//创建一个字符串对象,传入一个char *类型的字符串,返回一个cJSON结构体类型的指针。
cJSON *cJSON_CreateString(const char *string);//向数组对象中添加一个键值,传入参数array为cJSON *结构体类型的指针,为数组对象,item为添加如数字对象中的对象指针。
void cJSON_AddItemToArray(cJSON *array, cJSON *item);//向json对象中添加一对键值,object为json对象,string为加入一对键值中的name,item为加入一对键值中的value。
void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);//释放一个cJSON对象占用的内存空间。
void cJSON_Delete(cJSON *c)

(4)输出cJSON数据

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

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

使用的时候,只需要定义一个指针,然后让这个指针指向这个函数返回的地址即可。

(5)使用实例

//main.h
#include <stdio.h>
#include "cJSON.h"int main()
{cJSON *temp_item = cJSON_CreateObject();cJSON_AddNumberToObject(temp_item, "Temperature", 33.21);cJSON *ack_item = cJSON_CreateObject();cJSON_AddNumberToObject(ack_item, "ack", 0);cJSON*root = cJSON_CreateObject();cJSON_AddStringToObject(root, "method","thing.service.property.post");cJSON_AddStringToObject(root, "id", "device");cJSON_AddItemToObject(root, "sys",ack_item);cJSON_AddItemToObject(root, "params",temp_item);cJSON_AddStringToObject(root, "version","1.0.0");char *str = cJSON_Print(root);printf("%s\n", str);return 0;
}

运行结果:
在这里插入图片描述

五、cJSON数据解析

解析JSON数据的过程,其实就是将整个cJSON数据包剥离一个一个链表节点(键值对)的过程。
四个常用的cJSON解析函数:

(1)cJSON_Parse

cJSON *cJSON_Parse(const char *value);

**作用:**将一个JSON数据包,按照cJSON结构体的结构序列化整个数据包,并在堆中开辟一块内存存储cJSON结构体

**返回值:**成功返回一个指向内存块中的cJSON的指针,失败返回NULL

(2)cJSON_GetObjectItem

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

**作用:**获取JSON字符串字段值

**返回值:**成功返回一个指向cJSON类型的结构体指针,失败返回NULL

(3)cJSON_Delete

void   cJSON_Delete(cJSON *c);

作用:释放位于堆中cJSON结构体内存
返回值:无

#include <stdio.h>
#include "cJSON.h"int main()
{char * str = "{\\"method\":       \"thing.service.property.post\",\\"id\":   \"device\",\\"sys\":  {\\"ack\":  0\},\\"params\":       {\\"Temperature\":  33.21,\\"datime\":\"2022.5.4\"\},\\"version\":      \"1.0.0\"\}";cJSON *obj = cJSON_Parse(str);//将一个json格式字符串按照json格式结构序列化,存在链表里面cJSON *version_obj = cJSON_GetObjectItem(obj, "version");printf("version:%s\n", version_obj->valuestring);cJSON *params_obj = cJSON_GetObjectItem(obj, "params");printf("Temperature:%f\n", params_obj->child->valuedouble);//需要注意的是params 的值是一个对象,所以params_obj用了一个child指针指向了一个链表,该节点存放了所以如果要拿Temperature的值,就需要用child指针访问该对象printf("datime:%s\n", params_obj->child->next->valuestring);//如果要取data的值,就需要用next指向该链表的下一个节点。return 0;
}

运行结果:
在这里插入图片描述

注意事项

在进行json数据解析时,如果不知道你取的该节点的值是什么类型时,可以用该节点中type来判断,然后再用valuedouble,valuestring或valueint获取该节点的值。

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

(void) cJSON_Delete(cJSON *item);

http://chatgpt.dhexx.cn/article/F0U5SvP6.shtml

相关文章

25cJSON

JSON介绍 JSON(JavaScript Object Notation,JS对象表示法) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集&#xff0c;采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。…

【万字详解】cJSON解析

目录 1、通过README文件&#xff0c;初步了解cJSON&#xff1a; 1.1、头文件的开头和结尾&#xff1a; 1.2、头文件关于cJSON类型的宏定义 1.3、头文件中的extern 2、阅读并且分析cJSON源码 2.1、结构体struct cJSON&#xff08;算法设计思想&#xff09;&#xff1a; 2…

cJSON库用法详解

cJSON库用法详解 问题和需要注意的地方一、JSON、cJSON简介1. JSON 简介2. JSON 语法3. 开源库cJSON简介 二、使用cJSON构造JSON1. cJSON库函数介绍2. 使用cJSON构造JSON 三、使用cJSON解析JSON 由于c语言中&#xff0c;没有直接的字典&#xff0c;字符串数组等数据结构&#x…

cJSON使用详细教程 | 一个轻量级C语言JSON解析器

1. JSON与cJSON JSON —— 轻量级的数据格式 JSON 全称 JavaScript Object Notation&#xff0c;即 JS对象简谱&#xff0c;是一种轻量级的数据格式。 它采用完全独立于编程语言的文本格式来存储和表示数据&#xff0c;语法简洁、层次结构清晰&#xff0c;易于人阅读和编写&…

OSPF报文与LSA

1. OSPF报文 OSPF报文 Hello 报文、 DD 报文、 LSR 报文、 LSU报文、LSAck 报文 OSPF 头部 OSPF 用 IP来封装协议报文&#xff0c;协议号89&#xff0c;5种OSPF的报文具有相同OSPF 头部。 OSPF 头部中关注的字段主要有&#xff1a; version&#xff1a; IPv4 OSPFv2 值为2&…

OSPF中的LSA

LSA LSA的基本信息 LSA --- 链路状态通告 --- ospf协议在不同网络环境下产生的鞋带不同信息的载体 LSDB --- 链路状态数据库 SPF --- 最短路径优先算法 Type --- LSA的类型&#xff0c;在OSPFV2版本中&#xff0c;需要掌握的LSA类型一共有六中。 LinkState ID --- 链路状态…

OSPF中LSA相关内容

OSPF的LSA LSA — 链路状态通告 — OSPF协议在不同的网络环境下携带和传递的信息 LSDB — 链路状态数据库 SPF ---- 最短路径优先算法 [Huawei]dis ospf lsdb — 查看lsa信息 LSA头部&#xff08;之后的每条lsa信息都要携带此头部&#xff09; LSA头部内容&#xff1a; 1…

OSPF的Router-LSA和Network-LSA

文章目录 Router-LSARouter-LSA描述P2P网络Router-LSA描述MA网络或NBMA网络 Network-LSANetwork-LSA描述MA网络或NBMA网络 OSPF区域内LSDBSPF计算过程SPF算法构建SPF树计算最优路由查看OSPF路由表 单区域OSPF配置实现查看OSPF邻居状态 Router-LSA Router-LSA描述P2P网络 <R…

OSPF 之 6类LSA详解

目录 1类LSA&#xff1a;router -LSA 2类LSA &#xff0c;network LSA &#xff0c;网络LSA 3类LSA &#xff1a;summary LSA 汇总LSA 5类LSA&#xff1a; 外部LSA 4类LSA&#xff1a; summary ASBR LSA 7类LSA &#xff1a; NSSA LSA 1类LSA&#xff1a;router -LSA …

六类LSA及其作用

LSA&#xff08;链路状态通告&#xff09; LSA的组成 TYPE&#xff1a;LSA的类型&#xff0c;在OSPFV2中&#xff0c;需要掌握的有六种 LINK-State ID&#xff1a;链路状态标识符&#xff0c;用来标记一条LSA信息&#xff0c;相当于是一条LSA的名字 AdvRouter&#xff1a;通告…

图解 OSPF :什么是 LSA ?

大家好&#xff0c;我是小弗。我们都知道了&#xff0c;运行链路状态路由协议的路由器是交换链路状态信息。所有路由器都会生成自己直连接口状态的链路信息&#xff0c;并通告出去。路由器把在网络中收到的链路状态信息存入 LSDB&#xff08;链路状态数据库&#xff09;&#x…

ospf几种lsa

ospf网络类型 1.点到点 点到点网段 2.广播网络 transit网段&#xff08;至少有两台路由器的广播型网段&#xff09; 3.NBMA transit网段 4.点到多点 ospf网段的类型(网段的类型只与网络的类型有关) 1.transit网段 2.stub网段 3.点到点网段 4.virtual 一类的lsa rout…

OSPF的6种LSA

目录 一、6种LSA的简单介绍 1.1 1类LSA 1.1.1 1类LSA基础 1.1.2 1类LSA的报文格式&#xff1a; 1.1.3 1类LSA的链路类型&#xff08;link type&#xff09; 1.2 2类LSA 1.2.1 2类LSA基础 1.2.2 2类LSA的报文格式 1.3 3类LSA 1.3.1 3类LSA基础 1.3.2 3类LSA的报文…

LSA潜在语义分析

【转自&#xff1a;https://blog.csdn.net/roger__wong/article/details/41175967】 原文地址&#xff1a;http://en.wikipedia.org/wiki/Latent_semantic_analysis 前言 浅层语义分析&#xff08;LSA&#xff09;是一种自然语言处理中用到的方法&#xff0c;其通过“矢量语…

OSPF的LSA

文章目录 ospf的lsaLSA的头部信息一类LSA:Router二类LSA:Network三类LSA:Sum-net五类LSA&#xff1a;External四类LSA&#xff1a;Sum-Asbr ospf的lsa ospf本质是通过lsa(链路状态通告)洪泛&#xff0c;将运行ospf域内的所有lsa存放到本地lsdb(链路状态数据库)&#xff0c;然后…

LSA类型详解

在OSPF中有6种常用的LSA类型&#xff0c;分别为&#xff1a; Router-LSA&#xff08;1类&#xff09;、 Network- LSA&#xff08;2类&#xff09;、 Summary- LSA&#xff08;3类&#xff09;、 ASBR-Summary- LSA&#xff08;4类&#xff09;、 AS-External- LSA&#xff08;…

OSPF 之 LSA限制

目录 特殊区域 1.stub 区域&#xff0c; 末节区域 2.完全的末节区域 3.NSSA区域&#xff1a;&#xff08;not so stub area&#xff09; 非完全末节区域 4.完全的非完全的末节区域 LSA汇总 1. 3类LSA汇总&#xff1a; 2. 5类LSA 汇总&#xff1a; 3. 7类LSA 汇总&…

LSA类型

类型 LSID 通告者AdvRouter 作用范围 携带信息 Type-1LSA Router 通告者RID 区域内所有运行ospf协议的路由器的RID 单区域内部 拓扑信息&#xff0c;本地接口直连拓扑 Type-2LSA network DR接口的ip地址 DR锁在的路由器的RID 单区域内部 单个MA网络拓扑信息的补充…

OSPF协议总结5(六种LSA)

LSA----链路状态通告--- OSPF协议在不同网络环境下产生的携带不同信息的载体。 LSDB --链路状态数据库 SPF ---最短路径优先算法 查看LSDB数据库&#xff1a; Type --- LSA的类型&#xff0c;在OSPFV2版本中&#xff0c;需要掌握的L SA类型一共有6种。LinkState ID ---链路状态…

LSA笔记

http://www.360doc.com/content/22/0220/08/476286_1018188488.shtml 笔记 clear ip ospf process //慎用 &#xff0c;使用后会造成网络中断&#xff0c; 100&#xff0c;000&#xff0c;000bit //8次方比特&#xff0c;就是百兆速度&#xff0c; cost100M/接口带宽 hello&am…