cJSON库的使用

MQTT+JSON是物联网设备进行数据交互最常用的通讯协议,其中JSON协议非常适合用于明文传输、短包传输的场景。

认识理解和运用设计一个协议,一定要先了解其原理,再去认识其优缺点和适用范围

目录

1、JSON概念表述

2、cJSON库可能需要的修改

2.1 内存修改

2.2 打印number出现的科学计数法(参考)

3、JSON解析和处理的一般方法

3.1 定义

3.2 解析JSON

3.2 反馈/生成JSON

3.4 经常用到的JSON处理函数


1、JSON概念表述

JSON数据结构是一个键值对的序列化对象或数组。

键,是一个字符串;

值,可以是对象,数组,数字,字符串其中的一种;

其中对象由花括号括起来并用逗号进行分割;

数组是由方括号括起来的,字符串是由双引号包围的,二者都由逗号分隔;

数字不需要括起来,直接用逗号分隔。

其本质就是一个字符串,可以跨语言、跨平台进行存储,并且字符串key-value的形式方便阅读。


cJSON库就是一种最典型的C语言JSON库代码。

非常适合在嵌入式设备上移植和使用。

2、cJSON库可能需要的修改

2.1 内存修改

该库默认使用的C标准库的内存函数。

如果你需要调试内存问题,或者需要统一内存使用方式,都可以更改如下位置的代码替换内存函数。

#if defined(_MSC_VER)
/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
static void * CJSON_CDECL internal_malloc(size_t size)
{
    return malloc(size);
}
static void CJSON_CDECL internal_free(void *pointer)
{
    free(pointer);
}
static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
{
    return realloc(pointer, size);
}
#else
#define internal_malloc malloc
#define internal_free free
#define internal_realloc realloc
#endif

2.2 打印number出现的科学计数法(参考)

static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer);

文件第511行,该函数执行sscanf之后,会出现导致打印number类型数值,结果出现科学计数法的问题(stm32F205芯片调试时出现的,当时内存资源极度紧张)。

修改方案如下(修改后解决):

具体原因我也没搞清楚,就先列一下解决方案

#if 0
        /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
        length = sprintf((char*)number_buffer, "%1.15g", d);

        /* Check whether the original double can be recovered */
        if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
        {
            /* If not, print with 17 decimal places of precision */
            length = sprintf((char*)number_buffer, "%1.17g", d);
        }
#else
		length = sprintf((char*)number_buffer, "%d", (unsigned int)d);
#endif

3、JSON解析和处理的一般方法

3.1 定义

//头文件的必要包含关系和声明

#include "cJSON.h"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

typedef enum
{
	ERROR_CODE_INTER = -1,	//内部错误
	ERROR_CODE_NONE = 0,	//空/OK
	ERROR_CODE_PARAM,		//参数错误
} EnErrorCode;

typedef struct
{
	char *CmdStr;
	int (*CmdFun)(cJSON*);
} TyJsonCmdFun;

void JsonCmdAnalysis(uint8_t *pData, uint16_t unDataLen);
void JsonCmdResponse(cJSON *Json, int err, void *res);
void JsonCmdSend(uint8_t *pData, uint16_t unDataLen);

如上,定义了①函数返回的错误码,②json函数结构体,③函数解析和反馈的入口。

3.2 解析JSON

基于此,可以定义如下的函数表

收到数据去解析时,判断json的固定键值对-如"cmd"字段,来执行对应的处理函数。

这里需要注意一个点:cJSON_Parse和cJSON_Delete。

cJSON_Parse函数用于将字节流转成JSON对象,其中存在内存申请动作,且无法通过free进行释放,只能通过cJSON_Delete进行释放

int JsonCmdDeal_version(cJSON *Json);
int JsonCmdDeal_ota_start(cJSON *Json);
int JsonCmdDeal_ota_data(cJSON *Json);
int JsonCmdDeal_ota_end(cJSON *Json);
int JsonCmdDeal_driver_test(cJSON *Json);

const static TyJsonCmdFun JsonCmdFunTable[] = 
{
	{"version",			JsonCmdDeal_version},
	{"ota_start",		JsonCmdDeal_ota_start},
	{"ota_data",		JsonCmdDeal_ota_data},
	{"ota_end",			JsonCmdDeal_ota_end},
	{"driver_test",		JsonCmdDeal_driver_test},
}; 

void JsonCmdAnalysis(uint8_t *pData, uint16_t unDataLen)
{
	uint8_t i;
	cJSON *JsonCmd = NULL;
	
	JsonCmd = cJSON_Parse((char*)pData);
	if(JsonCmd != NULL)
	{
		for(i=0;i<sizeof(JsonCmdFunTable)/sizeof(TyJsonCmdFun);i++)
		{
			if(strcmp(JsonCmdFunTable[i].CmdStr,cJSON_GetObjectItem(JsonCmd,"cmd")->valuestring) == 0)
			{
				JsonCmdFunTable[i].CmdFun(JsonCmd);
				break;
			}
		}
		cJSON_Delete(JsonCmd);
	}
}

3.2 反馈/生成JSON

void JsonCmdResponse(cJSON *Json, int err, void *res)
{
	char *json_buf = NULL;
	int buf_len = 0;
	cJSON* respond_json = NULL;
	
	respond_json = cJSON_CreateObject();
	if(respond_json != NULL)
	{
		cJSON_AddStringToObject(respond_json, "cmd", cJSON_GetObjectItem(Json,"cmd")->valuestring);
	
		if(err == ERROR_CODE_NONE)
			cJSON_AddStringToObject(respond_json, "rsq", "ok");
		else
			cJSON_AddNumberToObject(respond_json, "rsq", err);
		
		json_buf = cJSON_PrintUnformatted(respond_json);
		if(json_buf != NULL)
		{
			buf_len = strlen((char*)json_buf);
			
			JsonCmdSend((uint8_t *)json_buf, buf_len);
			
			free(json_buf);
		}
		
		cJSON_Delete(respond_json);
	}
}

void JsonCmdSend(uint8_t *pData, uint16_t unDataLen)
{
	//do nothing
}

int JsonCmdDeal_version(cJSON *Json)
{
	int err = ERROR_CODE_NONE;
	
	JsonCmdResponse(Json, err, NULL);
	
	return err;
}

同样的,cJSON_CreateObject() 函数用于创建JSON对象,并随着后面用户调用cJSON_AddXXXXToObject类的函数产生内存申请(realloc),最终只能由cJSON_Delete释放内存。

额外的一点,就是cJSON的print函数,是可以直接由free函数进行释放的。

其中,cJSON_PrintUnformatted是将JSON对象打印成无格式化字符的字符串

cJSON_Print则是带有格式化字符的(会带有很多的空格与换行)。

如果需要直观查看JSON数据,建议用cJSON_Print;

但是如果是应用业务执行,建议还是用cJSON_PrintUnformatted,尽可能减少字节量传输。

3.4 经常用到的JSON处理函数

//内存操作函数
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);//字符串转json
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);//打印json字符串1
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);//打印json字符串2
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);//释放json

//创建json对象
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);//创建json对象
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);//创建数组

//创建json对象-用于更新键值对
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);

//获取json对象
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);//获取数组大小
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);//获取数组的某一元素
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);//获取键值对

//添加json键值对
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_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_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_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_IsFalse(const cJSON * const item);
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);

//替换(更新)键值对
CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猪熊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值