最近在做项目的时候遇到了JSON格式的数据。需要对Json格式的数据进行解析,而我所使用的设备是嵌入式单片机,因此需要借助cJson来将Json和C语言结合起来。这篇文章就是我对Json和cJson相关内容知识的一个总结。
首先,JSON为什么出现。Json是一种人们规定的数据交换格式。在现实生活中,我们需要进行数据交换的情景非常多,但是许多情况下,由于各个设备所采用的语言、架构不同,因此一个设备内部的数据、变量、结构体、对象等不能被其他设备所识别,这就好像一个中国人需要的数字格式是一、二、三,一个英国人需要的数字格式是one、two、three,这两个人是无法进行正常的数据交换的。
那么这个时候如果我们把我们所需要交换的数据,通过某种固定的格式,变成大家都能识别的语言,这样的话,数据的交换只交换大家都能识别的语言,每个设备就不需要考虑数据交换的对方的语言,而只需要对要交换的数据按照约定的格式进行打包和解析。就像中国人和英国人只需要各自将数字转化为1、2、3的格式,或者将1、2、3格式的数字转化为各自所需要的语言格式,这样两个人就能正常交流了。
那么我们已经知道了Json产生的原因,那么一个Json具体是什么样的呢?网上很多文章都提到了JSON的键、键值、数组等等,这样看起来JSON也很复杂,难以理解。其实我们抓住本质来说,JSON首先是一个纯的文本(字符串)。因为JSON是需要在各个编程语言中都能存在的,因此JSON的本质就是一堆文本。只不过因为这种文本为了方便打包和解析,我们定义了具体的写法和结构,因此我们可以给出定义:用于数据交换的、采用这种写法的、字符串、就叫JSON。
那么具体来说JSON是采用怎样的写法呢?也就是说,JSON的结构是什么。要弄清楚这个,首先我们要有一定的编程基础,知道在我们常用的语言中,例如C、C++、Java等,变量的种类都有什么。因为JSON的目的是将一个设备中的变量告诉给另个一设备,所以首先我们要知道我们需要在JSON中表达出来的变量的类型都有什么。然后当我们知道了我们需要表达的类型,我们再去知道通过什么样的格式来将变量构造成文本。
那么在常用的语言中,变量的种类基本上可以分为两种(这里我们不是讨论变量的具体形式例如整形、浮点型,而是谈论变量的表达方法),一种是对象,一种是数组。一个对象具有属性,每个属性又有该属性的属性值。例如某同学是一个对象,该同学具有的属性有姓名,年龄等,每个属性有其对应的属性值,“小明”、“15”,属性值可以是数值、字符串、数组、对象等形式。
那么我们如何把上述的对象转化为JSON格式的文本呢?
//实际交换时的JSON数据
{"student": { "name": "小明","age":15}}
//便于阅读的JSON数据
{
"student":
{
"name": "小明",
"age":15
}
}
入上图所示,“student”:表示对象,{ }表示对象的内容,对象内容里
"name"表示属性,“:”后面跟的是属性值,“,”表示并列的属性。通过这样的文本我们就将一个对象转化为了JSON数据,从而能够进行数据交换。
之前我们介绍到,数据的类型还有数组的形式,同样举例来说,加入我们现在面向整个班级,每个班级的学生都是一个对象,这么多个对象在一起组成一数组,这个数组就是我们所说的班级,那么如何把一个班的数据转化为JSON格式呢?
//实际交换时的JSON数据
{"class":{"class_number":1,"students":[{"name": "小明","age":15},{"name": "小红","age":14}, {"name": "小强","age":14}]}}
//便于阅读的JSON数据
{
"class":
{
"class_number":1,
"students":[
{
"name": "小明",
"age":15
},
{
"name": "小红",
"age":14
},
{
"name": "小强",
"age":14
}]
}
}
这里通过“[ ]”来表达数组。
总结来说,
1.首先一个JSON格式的文本以“{ }”为开头和结尾。
2.“{ }”内的为该对象所包含的属性。
3.属性加“ : ”的后面是该属性的属性值。
4.“,”表示相同对象中的不同属性,或相同数组里的不同个体。
5.“[ ]”表示数组。
6.数组和对象的区别在于,数组的成员是具有属性的不同个体,而对象的成员的是不同属性。
通过这种文本架构,我们就能将许多变量转化为我们所需要的JSON格式来进行数据交换。
到这里我们已经基本上对于JSON有了一个了解。此外补充几点,因为JSON的这种格式其实是在javascript中延伸出来的,可以存储Javascript复合对象,因此在这类平台中使用较为方便。
这里介绍一个网站可以进行JSON的数据在线解析。
https://www.json.cn/
以及JSON的说明网站
http://www.json.org/
接下来我们来将目光转向cJson。
Json在C语言中的使用存在一个问题,那就是C语言是一种面向过程的语言,和其他语言不同,因此C语言中没有对象等定义。那么Json在C语言的平台的使用过程中,就需要进行一些独属于C语言的解析和打包方法,这种基于C语言的JSON的处理方法我们称之为cJson。
目前已经有别人写好的cJSON的开源代码供我们使用,这样我们就不用花时间在造轮子上,而只需要学会如何取使用。因此接下来我们重点介绍cJSON的使用。
首先我们要在工程里包括cJson.c和cJson.h文件。这两个文件可以在网上自己找。或者我的下载里也有。
然后我们先从cJson结构体来入手,了解如何用C语言打包一个Json和如何用C语言解析一个Json。
上文所说。C语言没有对象这一概念,因此我们定义了一个结构体,我们称这个结构体为cJson结构体,从而将对象转化为了CJSON格式的结构体。从而在C语言中,我们打包或者解析一个JSON,经历的步骤为:需要打包的变量<——>cJson结构体<——>Json文本
从.h文件我们可以找到一个cJson结构体的形式:
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
这里一个cjson结构体包含变量有:
struct cJSON *next,*prev; //指向上一个/下一个cJSON的指针,用于链表结构
struct cJSON *child; //指向该cJson的子对象的cJson指针
int type; //该cJson结构体所包含的项目的类型
char *valuestring; //这个项目的值:字符串。如果type=cJSON_String
int valueint; //这个项目的值:int数字。如果type=cJSON_Number
double valuedouble; //这个项目的值:double数字,如果type=cJSON_Number
char *string; //该项目的名称(字符串),如果该项目在对象的子项列表中或为对象的子项
那么还是以上文为例,如何把上述的JSON文本:
{
"student":
{
"name": "小明",
"age":15
}
}
转换成cJSON的结构体?
答案就是,首先把总的JSON作为一个结构体,假设总的cJSON名字为cJSON_0,那么cJSON_0的成员为:
cJSON_0
{
next=0 //因为没有数组类的结构,所以向前向后指针都为0
prev=0 //因为没有数组类的结构,所以向前向后指针都为0
child=(某地址,指向该cJSON的子系,假设这个子系cJSON为cJSON_0_0)
type=cJSON_Object //表明这个cJSON表示的是一个具有内部属性的对象
valuestring=0
valueint=0
valuedouble=0
string=0
}
//总的cJSON结构体种类为cJSON_Object,表明该结构体代表的是一个JSON对象
然后我们打开这个cJSON_0的子系所指的结构体,发现该结构体的成员为:
cJSON_0_0
{
next=0 //因为没有数组类的结构,所以向前向后指针都为0
prev=0 //因为没有数组类的结构,所以向前向后指针都为0
child=(某地址,指向该cJSON的子系,假设这个子系cJSON为cJSON_0_0_0)
type=cJSON_Object //表明这个cJSON表示的是一个具有内部属性的对象
valuestring=0
valueint=0
valuedouble=0
string="student" //该项目名为“student”
}
//该cJSON表示的是“student”这个项目,项目类型为cJSON_Object,因为该项目是一个对象,这个对象还有其内在的属性
然后再打开该cJSON的子系,即cJSON_0_0_0,其成员为
cJSON_0_0_0
{
next=(下一个同级的cJSON结构体,假设为cJSON_0_0_1 ) //因为这里是有和“name”项目同级的“age”存在
prev=0 //因为没有数组类的结构,所以向前向后指针都为0
child=0 //因为这个是最内层的项目,没有子项目了,所以无子系,子系的指针为0
type=cJSON_String //表明该cJSON表示的是一个字符串
valuestring="小明" //字符串的值
valueint=0
valuedouble=0
string="name" //表明该项目的名称为“name”
}
//该cJSON表示的是“student”这个项目,项目类型为cJSON_Object,因为该项目是一个对象,这个对象还有其内在的属性
以及next指针指向的下一个cJOSN结构体cJSON_0_0_1:
cJSON_0_0_1
{
next=0 //因为这个是该层的最后一个
prev=(指向cJSON_0_0_0) //指向上一个同级的cJSON结构体
child=0 //因为这个是最内层的项目,没有子项目了,所以无子系,子系的指针为0
type=cJSON_Number //表明该cJSON表示的是一个数
valuestring=0
valueint=0x0F //表示15
valuedouble=15.0 //表示15
string="age" //表明该项目的名称为“age”
}
//该cJSON表示的是“student”这个项目,项目类型为cJSON_Object,因为该项目是一个对象,这个对象还有其内在的属性
通过这种cJSON结构体,就把JSON文本中的各个项目嵌套的关系表现了出来。即
{
"student":
{
"name": "小明",
"age":15
}
}
将上述JSON转化为了具有父子关系和同辈关系的总共四个cJSON结构体
cJSON_0 :总的JSON项目,类型为Object,包含cJSON_0_0
cJSON_0_0 :项目名“student”,类型为Object,包含cJSON_0_0_0
cJSON_0_0_0 :项目名“name”,类型为String,值为“小明”,有下一个cJSON指针cJSON_0_0_1
cJSON_0_0_1 :项目名“age”,类型为Number,值为15,有上一个cJSON指针cJSON_0_0_0
以上的所有数值都是经过了debug验证过的。
接下来我们将介绍在cJSON.c中,如何实现JSON和cJSON之间的相互转化。
这里用到了cJSON的两个比较重要的函数分别是:
extern cJSON *cJSON_Parse(const char *value);
//函数名:cJSON_Parse()
//功能:cJSON解析函数,将JSON转化为cJSON结构体
//输入:JSON文本的指针
//返回值:最外层的cJSON结构体的指针
//注意事项:当cJSON使用完成后,需要手动调用cJSON_Delete()函数来释放内存
以及
extern char *cJSON_Print(cJSON *item);
//函数名:cJSON_Print()
//功能:cJSON打包函数,将cJSON结构体转化为JSON文本
//输入:最外层的cJSON结构体的指针
//返回值:转化后的JSON文本的指针
//注意事项:当JSON文本发送完成后,需要手动free掉JSON文本所占用的内存
此外,我们还可能用到一些辅助上述两个函数的其他函数,例如:
extern void cJSON_Delete(cJSON *c);
//函数名:cJSON_Delete()
//功能:删除cJSON结构体
//输入:最外层的cJSON结构体的指针
//返回值:无
以及
extern const char *cJSON_GetErrorPtr(void);
//函数名:cJSON_GetErrorPtr()
//功能:当cJSON解析出错时,获得解析失败处的字符的指针
//输入:无
//返回值:解析失败处的字符的指针
相应的解析和打包的代码如下:
cJSON* A; //定义一个cJSON结构体变量 A
char* B; //定义一个字符串 B
char* C="{"student": { "name": "小明","age":15}}"; //定义一个字符串 C,假设C就是我们所接收到的JSON
A=cJSON_Parse(C); //把C解析成cJSON结构体,幅值给A
if(A == NULL ) //A=NULL表明解析失败
{
printf("Error before: [%s]\n",cJSON_GetErrorPtr()); //从解析失败处开始将剩下的字符打印出
}
else //解析成功,A为cJSON结构体
{
B=cJSON_Print(A); //把A的cJSON结构体打包成JSON字符串
if(B == NULL) //打包失败
{
u2_printf("CJSON Print error"); //打印"CJSON Print error"
}
else u2_printf("%s /r/n",B); //解析成功,将打包后的JSON字符串打印出来
}
cJSON_Delete(A); //删除cJSON结构体
free(B); //释放B所占的内存
此外,补充一点,上述的打包和解析对于数组也是一样的,只是数组对应的cJSON里的type为cJSON_Array。
到这里,我们已经知道了如何把JSON解析为cJSON结构体。距离我们上文所说的:需要打包的变量<——>cJson结构体<——>Json文本,就还差 “ 变量<——>cJson结构体 ” 需要完成。这里就是涉及到我们的具体使用情景了。这里为了叙述方便,我们把从cJSON结构体到具体变量叫做提取,把从具体变量到cJSON叫做构成。
首先我们先将,如何从cJSON结构体中提取我们所想要的某个项目的项目值。(这里我们是在已知JSON格式,但是其中的属性值未知的的情况下谈论这些事情的)
这里也是用到了几个库函数,如下所示:
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
//函数名:cJSON_GetObjectItem();
//作用:在大一层的cJSON结构体中,根据给定的项目名,得到该项目名的cJSON结构体
//输入:cJSON* object,数据类型为cJSON_Object的cJSON结构体
// char * string,我们所需要的项目的项目名
//输出:我们所要求的项目的cJSON结构体的指针
// 如果想要提取该项目的项目值,可以利用结构体的格式"->"来取到结构体内部的值,具体的使用方法我们下面会有实例讲解
//这里应该注意的地方是,该函数只能对包含该项目名的最小的那个cJSON_Object进行提取。假如说从一个大的cJSON中找到我们所需要的那一项,这个过程就像剥洋葱一样一层cJSON一层cJSON的提取,那么这个cJSON_GetObjectItem()函数一次调用只能够剥一层皮。
然后是当我们需要提取数组类型的cJSON时,需要用到以下两个函数:
extern int cJSON_GetArraySize(cJSON *array);
//函数名:cJSON_GetArraySize()
//功能:得到cJSON数组中的成员的个数
//输入:cJSON *array ,数据类型为cJSON_Array的cJSON结构体
//返回值:数组内的成员个数
//这里需要注意的是,输入的数组一定要是一个cJSON_Array型的cJSON结构体
以及
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
//函数名:cJSON_GetArrayItem()
//功能:根据下标获得cJSON数组中的成员的指针
//输入:cJSON *array ,数据类型为cJSON_Array的cJSON结构体
// int item,成员的下标,从0到size-1共size个成员
//返回值:某下标的成员的cJSON结构体指针
//这里需要注意的是,输入的数组一定要是一个cJSON_Array型的cJSON结构体
对于上述三个函数的应用,我们也是通过一个实例来表现:
cJSON* A; //定义一个cJSON结构体变量 A
cJSON* AA; //定义一个cJSON结构体变量,该变量用于表达A的子系
cJSON* AAA; //定义一个cJSON结构体变量,该变量用于表达AA的子系
cJSON* AAAA; //定义一个cJSON结构体变量,该变量用于表达AAA的子系
cJSON* NAME; //item名为name的cJSON
cJSON* AGE; //item名为age的cJSON
char* name;
u8 age;
u8 n,i;
char* C="{"class":{"class_number":1,"students":[{"name": "小明","age":15},{"name": "小红","age":14}, {"name": "小强","age":14}]}}"; //定义一个带有数组的字符串 C,假设C就是我们所接收到的JSON
A=cJSON_Parse(C);
if(A == NULL )
{
printf("Error Parse before: [%s]\n",cJSON_GetErrorPtr());
}
else
{
AA=(cJSON_GetObjectItem(A,"class"));
AAA=(cJSON_GetObjectItem(AA,"students"));
n=cJSON_GetArraySize(AAA); //获得数组成员个数
u2_printf("arr_num=%u \r\n",n);
delay_ms(50);
for(i=0;ivaluestring;
u2_printf("student%u_name=%s \r\n",i,name);
}
delay_ms(50);
AGE=cJSON_GetObjectItem(AAAA,"age");
if(AGE==NULL) u2_printf("ERROR AGE \r\n");
else
{
age=AGE->valueint;
u2_printf("student%u_age=%u \r\n",i,age);
}
delay_ms(50);
}
cJSON_Delete(A); //删除cJSON结构体
通过上面三个函数,我们就能够将已经解析好的cJSON的信息提取出来,得到其各个项目的属性值。这样就完成了提取这一操作,从而将cJSON转化为了变量。
接下来我们需要谈论的就是,如何将变量组合成cJSON的结构体。如果我们要把某些变量作为JSON文本发出去,我们的步骤是,先将我们要发的所有变量组合成cJSON结构体的格式,然后我们在通过上述的cjson_print,将cJSON结构体转化为了JSON文本并发送。后面这一步。
这里也是主要用一些库函数来实现。下面也是先介绍库函数,然后再通过一个实例来实现cJSON结构体的构造。
首先是构造cJSON结构体常用的库函数:
首先是创造cJSON结构体函数,它根据所创造的cJSON结构体的类型不同,而采用不同的函数,这里我们就不一一介绍这些函数。
/* These calls create a cJSON item of the appropriate type. */
extern cJSON *cJSON_CreateNull(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);
//cJSON_CreateNull(); 创造NULL类型的cJSON结构体,不需要输入值
//cJSON_CreateTrue(); 创造TRUE类型的cJSON结构体,不需要输入值
//cJSON_CreateFalse(); 创造FALSE类型的cJSON结构体,不需要输入值
//cJSON_CreateBool(); 创造BOOL类型的cJSON结构体,输入值为1或0
//cJSON_CreateNumber(); 创造NUMBER类型的cJSON结构体,输入值为double
//cJSON_CreateString(); 创造STRING类型的cJSON结构体,输入值为字符串指针
//cJSON_CreateString(); 创造ARRAY类型的cJSON结构体,不需要输入值
//cJSON_CreateString(); 创造ARRAY类型的cJSON结构体,不需要输入值
//这里cJSON_CreateString()和cJSON_CreateString()不需要输入值,但是需要采用其他函数来对成员进行添加。
//上述所有的函数输出为我们所创造得到的cJSON的结构体的指针
此外在进行数组创造的时候,也可以直接将我们需要组合的item直接生成为一个ARRAY类型的cJSON结构体。主要是以下几个函数:
/* These utilities create an Array of count items. */
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);
//cJSON_CreateIntArray();创造一个成员为int型的array型cJSON
以及向现有的Object/Arrary里添加其他item。
/* Append item to the specified array/object. */
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
//cJSON_AddItemToArray(); //在一个Array型的cJSON里添加cJSON成员
//cJSON_AddItemToObject(); //在一个Object型的cJSON里添加cJSON成员,这里输入值string为要添加的cJSON的键值
//cJSON_AddItemToObjectCS(); //在一个Object型的cJSON里添加cJSON成员,这里输入值string为要添加的cJSON的键值,且该键值是一个const string,所以在cjson_delet();执行的时候,会跳过删除该字符串。
//此外还有带有引用性质的构成函数
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
//cJSON_AddItemReferenceToArray(); //用一个item来引用一个array型cJSON,在删除该item时,不会删除其引用的array型cJSON
//cJSON_AddItemToObject(); //用一个item来引用一个object型cJSON,在删除该item时,不会删除其引用的object型cJSON
以及从cJSON结构体里删除指定的item
/* Remove/Detatch items from Arrays/Objects. */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/cJSON_DetachItemFromArray(); 从array的cJSON结构体中将指定下标的成员分离出来。返回的是分离出来的成员的cJSON指针。为避免内存泄漏,确保将返回值赋值给一个指针
//cJSON_DeleteItemFromArray(); 从array的cJSON结构体中将指定下标的成员删除。
//cJSON_DetachItemFromObject(); 从object的cJSON结构体中将指定下标的成员分离出来。返回的是分离出来的成员的cJSON指针。为避免内存泄漏,确保将返回值赋值给一个指针
//cJSON_DeleteItemFromObject(); 从object的cJSON结构体中将指定下标的成员删除。
以及提供了可以将cJSON中的成员替换或增加其他cJSON结构体的函数:
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem);
//cJSON_ReplaceItemInArray(); 根据下标替换数组类型的cJSON的成员
//cJSON_ReplaceItemInArray(); 根据键名替换OBJECT类型的cJSON的成员
//cJSON_InsertItemInArray(); 可以将一个新元素插入数组中的某下标的位置,原来位置的成员的下标依次向下顺移
如何构成一个较大的cJSON结构体,还是以我们之前的内容为例:
//假设我们要的cJSON结构体是
{
"student":
{
"name": "小明",
"age":15
}
}
那么我们的代码如下:
cJSON * A=cJSON_CreateObject();
cJSON * AA=cJSON_CreateObject();
char* out;
cJSON_AddItemToObject(AA, "name", cJSON_CreateString("小明"));
cJSON_AddItemToObject(AA, "age", cJSON_CreateNumber(15));
cJSON_AddItemToObject(A, "student", AA);
out=cJSON_Print(A);
printf("%s \r\n",out);
//假设我们要的cJSON结构体为数组形式的:
{
"class":
{
"class_number":1,
"students":[
{
"name": "小明",
"age":15
},
{
"name": "小红",
"age":14
},
{
"name": "小强",
"age":16
}]
}
}
//那么我们的代码为
cJSON * A=cJSON_CreateObject();
cJSON * AA=cJSON_CreateObject();
cJSON * AAA=cJSON_CreateArray();
cJSON * STUDENT1=cJSON_CreateObject();
cJSON * STUDENT2=cJSON_CreateObject();
cJSON * STUDENT3=cJSON_CreateObject();
char* out;
cJSON_AddItemToObject(STUDENT1, "name", cJSON_CreateString("小明"));
cJSON_AddItemToObject(STUDENT1, "age", cJSON_CreateNumber(15));
cJSON_AddItemToObject(STUDENT2, "name", cJSON_CreateString("小红"));
cJSON_AddItemToObject(STUDENT2, "age", cJSON_CreateNumber(14));
cJSON_AddItemToObject(STUDENT3, "name", cJSON_CreateString("小强"));
cJSON_AddItemToObject(STUDENT3, "age", cJSON_CreateNumber(16));
cJSON_AddItemToArray(AAA,STUDENT1);
cJSON_AddItemToArray(AAA,STUDENT2);
cJSON_AddItemToArray(AAA,STUDENT3);
cJSON_AddItemToObject(AA, "class_number", cJSON_CreateNumber(1));
cJSON_AddItemToObject(AA, "students", AAA);
cJSON_AddItemToObject(A, "class", AA);
out=cJSON_Print(A);
printf("%s \r\n",out);
至此我们的解析基本上告一段落,下面是我在进行STM32移植cJSON时遇到的一些具体的问题和解决方法:
1.cJSON_Parse();函数的返回值一直是0
原因,可能是因为接收到的文本不是标准的JSON格式。建议可以在后面加一个判断,判断返回值是否为NULL,如果是,调用cJSON_GetErrorPtr();函数来获知在哪里运行出错。如果不是,表明对JSON的解析完成。
2.cJSON_Print();函数的返回值一直是0
原因,可能是因为malloc不成功的原因。也就是给cJSON分配的内存较小。在STM32里,如果不是用自己编写的malloc函数,而是用的通用的malloc函数,那么需要在keil的Option->Target界面把“Use MiceoLIB”勾选。即使用微函数。
并且加入Print函数对于个数较小的JSON能够打印,但是对于个数较多的JSON不能打印,则还需要在文件中寻找Heap_Size变量,然后将后面的数字改大一些,以给cJSON分配更多的内存。
如下:
3.cJSON_GetObjectItem();函数的返回值为0
原因:返回值为0表明没有找到该键名对应的cJSON结构体。因为cJSON_GetObjectItem()只能对get到object的子系,不能get到object的子系的子系。也就是上文我们所说的,剥洋葱只能剥一层这个概念。所以注意是不是所写的键名是更内层的cJSON。
4.cJSON_ReplaceItemInObject函数的使用
对于cJSON_ReplaceItemInObject()函数,会把被替换的cJSON给删除,因此应避免重复删除,以及,对于只是想删除Object中对某一个cJSON的引用,而不想删除这个cJSON个体,此时就不能使用cJSON_ReplaceItemInObject()函数,而cjson.c中也没有相应操作的库函数,因此这里我们可以通过两种方法,对应的两个函数,cJSON_AddItemReferenceToObject();
cJSON_DetachItemFromObject();
一个是带引用性质的添加,这种添加在进行删除的时候,是不会删除引用的cJSON的。
一个是将cJSON从Object中分离出来,这种分离也是不删除的,因此这里会返回一个cJSON指针,以避免内存泄露。