首先看结构
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