Json和cJson的学习总结以及在STM32里移植cJSON需要注意哪些问题

最近在做项目的时候遇到了JSON格式的数据。需要对Json格式的数据进行解析,而我所使用的设备是嵌入式单片机,因此需要借助cJson来将Json和C语言结合起来。这篇文章就是我对Json和cJson相关内容知识的一个总结。

文章目录

      • 1 JSON的介绍
        • 1.1 JSON产生的原因
        • 1.2 JSON的格式
      • 2 cJSON的介绍
        • 2.1 什么是cJSON,什么是cJSON结构体
        • 2.2 如何将JSON文本转化为cJSON结构体之间的相互转化
        • 2.3 如何提取cJSON结构体中的变量
        • 2.4 如何将变量构成为cJSON结构体
      • 3 在STM32里移植cJSON遇到的问题和解决方法

1 JSON的介绍

1.1 JSON产生的原因

首先,JSON为什么出现。Json是一种人们规定的数据交换格式。在现实生活中,我们需要进行数据交换的情景非常多,但是许多情况下,由于各个设备所采用的语言、架构不同,因此一个设备内部的数据、变量、结构体、对象等不能被其他设备所识别,这就好像一个中国人需要的数字格式是一、二、三,一个英国人需要的数字格式是one、two、three,这两个人是无法进行正常的数据交换的。
那么这个时候如果我们把我们所需要交换的数据,通过某种固定的格式,变成大家都能识别的语言,这样的话,数据的交换只交换大家都能识别的语言,每个设备就不需要考虑数据交换的对方的语言,而只需要对要交换的数据按照约定的格式进行打包和解析。就像中国人和英国人只需要各自将数字转化为1、2、3的格式,或者将1、2、3格式的数字转化为各自所需要的语言格式,这样两个人就能正常交流了。

1.2 JSON的格式

那么我们已经知道了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/

2 cJSON的介绍

2.1 什么是cJSON,什么是cJSON结构体

接下来我们来将目光转向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验证过的。

2.2 如何将JSON文本转化为cJSON结构体之间的相互转化

接下来我们将介绍在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。

2.3 如何提取cJSON结构体中的变量

到这里,我们已经知道了如何把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转化为了变量。

2.4 如何将变量构成为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时遇到的一些具体的问题和解决方法:

3 在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”勾选。即使用微函数。
Json和cJson的学习总结以及在STM32里移植cJSON需要注意哪些问题_第1张图片
并且加入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指针,以避免内存泄露。

你可能感兴趣的:(STM32)