cJSON在8266中应用及cJSON解析总结

首先看结构

cJson 是c语言编写的一个解析器. 是一个超轻巧,携带方便,单文件,简单的可以作为ANSI-C标准的JSON解析器。主要两个文件cJSON.c (放user下)和cJSON.h(放入include下) 其他文件夹就需要修改配置文件了. 主要用来编码和解析数据.

其中,定义了一个cJSON的数据结构,用来储存数据.是以链表的形式.结构体如下:

在.h文件下

typedef struct cJSON {

struct cJSON *next,*prev; //双向链表

struct cJSON *child; //指向字节点,主要是在数组结构中用

int type; //类型,有7种类型,也就是说可以储存7种形式数据

//分别为:

1. cJSON_False

2. cJSON_True

3. cJSON_NULL

4. cJSON_Number

5. cJSON_String

6. cJSON_Array

7. cJSON_Object

char *valuestring; // 如果类型为cJSON_String 时用来存那个字符串

int valueint; //如果类型为cJSON_Number 时用来存值 ,这个主要存int型,如果是小数会有类型转换

double valuedouble; //这个存浮点数,如果是整数也会转换为浮点型,和valueint一起存同一个数据

char *string; //用来存字节点名字.主要是object时用.

} cJSON;


             
二. 把数据组织成结构(也就是节点组成结构)

下面的函数来产生各种类型的节点.

extern cJSON *cJSON_CreatNULL(void);

extern cJSON *cJSON_CreateTrue(void);

extern cJSON *cJSON_CreateFalse(void);

extern cJSON *cJSON_CreateBool(int b);

extern cJSON *cJSON_CreateNumber(double num);

extern cJSON *cJSON_CreateString(const char *string);

extern cJSON *cJSON_CreateArray(void);

extern cJSON *cJSON_CreateObject(void);

这些函数主要是把类型type写入节点,并且把相关的值也写入节点.比如创造字符串节点函数,还要把字符串指针valuestring指向相应字符串.

 

下面的函数创造数组节点集.也就是先创建一个数组类型的节点(用cJSON_CreateArray(void)函数 ),再把number数组的数据分别放在一个结点中(比如一个个int型数据的节点),并链表串起来(用suffix_object()函数).最后都悬挂在数组节点的字节点上(也就是结构体中的child指针指向它们),最后的最后返回数组的指针.

extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);

extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);

extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);

extern cJSON *cJSON_CreateStringArray(const char **strings,int count);

 

下面图例一个说明创建一个数型数组的过程:

 

三. 打包结构数据

组织好数据后就要已一定的格式把结构组织成字符串,便于传递. 使用函数:

extern char *cJSON_Print(cJSON *item);

extern char *cJSON_PrintUnformatted(cJSON *item);

 

1、环境:安信可IDE,cJSON版本1.6

2、主要注意事项

一、添加文件
在cJSON.c里添加下面头文件,这些头文件是ESP8266专用的。

#include "c_types.h"
#include "osapi.h"
#include "mem.h"
#include "ets_sys.h"
#include "osapi.h"

二、替换malloc和free
注释掉cJSON_malloc和cJSON_free,用ESP8266自带的os_malloc和os_free代替,由于直接赋值会编译出错,所以用了宏定义的方式。

#if 0
static void *(*cJSON_malloc)(size_t sz) = os_malloc;
static void (*cJSON_free)(void *ptr) = os_free;
#else
#define cJSON_malloc(sz) os_malloc(sz)
#define cJSON_free(ptr)  os_free(ptr)
#endif

三、注释钩子函数
其后,注释掉cJSON_InitHooks里面的内容,这个一般用不到所以注释掉也没关系。

void ICACHE_FLASH_ATTR
cJSON_InitHooks(cJSON_Hooks* hooks)
{
#if 0
    if (!hooks) { /* Reset hooks */
        cJSON_malloc = malloc;
        cJSON_free = free;
        return;
    }

    cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
    cJSON_free   = (hooks->free_fn)?hooks->free_fn:free;
#endif
}

四、替换其他函数
替换os_malloc和os_free后,还要替换printf,memset,memcpy等等,请对照osapi.h里的内容替换。

五、注释浮点有关语句
注释掉所有和浮点型有关的语句,ESP8266不支持浮点型,所以我们也不需要它,免得编译出错。例如注释下面的语句

/* Parse the input text to generate a number, and populate the result into item. */
static const char *ICACHE_FLASH_ATTR
parse_number(cJSON *item,const char *num)
{
    double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;

    if (*num=='-') sign=-1,num++;   /* Has sign? */
    if (*num=='0') num++;           /* is zero */
    if (*num>='1' && *num<='9') do  n=(n*10.0)+(*num++ -'0');   while (*num>='0' && *num<='9'); /* Number? */
    if (*num=='.' && num[1]>='0' && num[1]<='9') {num++;        do  n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');}  /* Fractional part? */
    if (*num=='e' || *num=='E')     /* Exponent? */
    {   num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++;      /* With sign? */
        while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');   /* Number? */
    }

    //注释掉这条语句
    //n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */

    item->valuedouble=n;
    item->valueint=(int)n;
    item->type=cJSON_Number;
    return num;
}

同样,找到有fabs函数和floor函数的语句,一并注释掉。 
注释掉导入float.h的语句。

六、添加宏定义
所有的函数,函数名前面都要添加ICACHE_FLASH_ATTR宏定义,这个是让函数保存在Flash,需要用到的时候才读取到cache中运行,不加这个宏定义,会报内存不够用的错误。示例:

const char * ICACHE_FLASH_ATTR
cJSON_GetErrorPtr(void) {return global_ep;}

别忘了test.c文件的函数也要添加。

七、改函数名
把test.c里的main函数改成其他函数名,例如cJSON_TEST。之后在user_init初始化函数里调用这个函数即可。

int ICACHE_FLASH_ATTR
cJson_update2(u8* jsondata) {
    const cJSON *token = NULL;
    const cJSON *path = NULL;
    const cJSON *version = NULL;
    const cJSON *ip = NULL;
    const cJSON *port = NULL;

	//u8* data = "{\"Token\":\"3b5677b02973e0ff2dc36a85ecb8c9e3\",\"Path\":\"updatebin\",\"Version\":\"1.2\",\"IP\":\"122.114.233.66\",\"Port\":81}";
	cJSON * root = cJSON_Parse(jsondata);
	if (NULL != root)
	{
		if (cJSON_HasObjectItem(root, "Token")) {
			token = cJSON_GetObjectItem(root, "Token");
			if (cJSON_IsString(token)) {//&& (name->valuestring != NULL
				char *s = cJSON_Print(token);
				os_printf("string token: %s\r\n", s);
				cJSON_free((void *) s);
			}
		}
		if (cJSON_HasObjectItem(root, "Path")) {
			path = cJSON_GetObjectItem(root, "Path");
			if (cJSON_IsString(path)) {//&& (name->valuestring != NULL
				char *s = cJSON_Print(path);
				os_printf("string path: %s\r\n", s);
				cJSON_free((void *) s);
			}
		}

		if (cJSON_HasObjectItem(root, "Version")) {
			version = cJSON_GetObjectItem(root, "Version");
			if (cJSON_IsString(version)) {
				char *s = cJSON_Print(version);
				os_printf("string version: %s\r\n", s);
				cJSON_free((void *) s);
			}
		}
		if (cJSON_HasObjectItem(root, "IP")) {
			ip = cJSON_GetObjectItem(root, "IP");
			if (cJSON_IsString(ip)) {
				char *s = cJSON_Print(ip);
				os_printf("string ip: %s\r\n", s);
				cJSON_free((void *) s);
			}
		}

		if (cJSON_HasObjectItem(root, "Port")) {
			port = cJSON_GetObjectItem(root, "Port");
			if (cJSON_IsNumber(port)) {
				char *s = cJSON_Print(port);
				os_printf("Number port: %s\r\n", s);
				cJSON_free((void *) s);
			}
		}

		cJSON_Delete(root);
	} else {
		os_printf("\r\nparse error!\r\n");
	}
}
//--------------------------------------------------------------------------------
调用方法:	
os_printf("\r\n=====================\r\n");
os_printf("\tcJSON Version: %s", cJSON_Version());
os_printf("\r\n=====================\r\n");

	//create_objects();//创建json实例

//	os_printf("\r\nparse string!========update======\r\n");
cJson_update2("{\"Token\":\"3b5677b02973e0ff2dc36a85ecb8c9e3\",\"Path\":\"updatebin\",\"Version\":\"1.1\",\"IP\":\"122.114.233.66\",\"Port\":81}");//测试
//	os_printf("parse string!========update======\r\n");



//输出结果举例
**==========UPDATE BIN BEGIN==========**
string token: "3b5677b02973e0ff2dc36a85ecb8c9e3"
string path: "updatebin"
string version: "1.2"
string ip: "122.114.233.66"
Number port: 81

**==========UPDATE BIN END!!========**

1、第一次测试成功了,后来改造之后出现为空现象

if (cJSON_HasObjectItem(root, "Token")) {。。。。} 结果无法满足条件总返回0,

问题所在:static void cJson_update(u8* jsondata); 声明前加static变成静态函数才行。类似问题出现过其他变量无法得到实际想要的结果。

2、定义结构体(至少在某.c文件),目的解析后的数据便于调用比较(我的测试举例)

//OTA升级结构体
struct updateBin_arr{
	const char *token;
	const char *path;
	const char *version;
	const char *ip;
	const char *port;
}stru_bin;


如果编译后还有问题,请自行调试。

其他
如果想了解如何使用ESP8266官方SDK里的JSON API,可以参考这篇文章:http://blog.csdn.net/yannanxiu/article/details/50911357

更新
更新cJSON版本到1.5.3,基于ESP8266 NONOS SDK 2.0测试。(大部分例子还是可以参考的)

Github:https://github.com/AngelLiang/ESP8266-Demos
 

 

参考:

1)https://blog.csdn.net/yannanxiu/article/details/52713746

2)https://www.jianshu.com/p/4fcb49b55ff6

3)https://blog.csdn.net/fengxinlinux/article/details/53121287

你可能感兴趣的:(个人作品,网络笔记,物联网笔记,学习笔记)