按照通常学习的思路,将对Json的学习分为4步,如下:
一、了解Json格式;
二、解析Json格式;
三、创建Json格式;
四、使用标准接口;
JSON 是基于纯文本的数据格式,我们可以用 JSON 传输一个简单的 String,Number,Boolean,也可以传输一个数组,或者一个复杂的 Object 对象。Json可以理解为一种规范化的键值对(key:value)描述方法,同 XML 相比,JSON 提供了更好的简单性和灵活性。
对于JSON的定义,在网上能找到许多,在其官方网站http://www.json.org.cn/ 有详细的介绍。
光看看Json的定义,还是远远不够的,下面就来动一下手,用C语言写个程序来解读一下Json字符串。
我是在Linux下编程的,但是对于C与Json,都是与平台无关的,所以windows平台上编程也是一样适用的。
Json使用的通常情况,分为两种,一个是做为接口,在两个进程之间传递信息;一个是做为存储信息,在程序启动之初读取后做初始化使用。
为了便于测试,我这里是手工写好一个符合Json格式的文本文件,然后进行读取解析。
这里主要是实现对key的解析,以及对value的解析。value又分为几种情况,我只考虑3种情况,string,int,float,这样,对于简单的应用,也是足够使用了的。
解析第一个Json字符串,如下:
{"firstName":"Brett"}
或者如下,一个数字的例子:
{"age":8}
要读取的内容如下:
{"firstName":" Brett ","lastName":"Jason","email":"[email protected]","age":8,"height":1.67}
要读取的内容是一个对象,我们需要定义一个与之匹配的结构体,如下:
typedef struct
{
charfirstName[32];
charlastName[32];
charemail[64];
intage;
floatheight;
}people;
需要注意,结构体中的成员在Json串中出现的顺序,不一定与结构体中的成员定义顺序一致(例如,age可能出现在第一个位置)。说到这里,其实就是提出了一个问题,在我们写的程序中,需要考虑哪些异常情况?例如,结构体中成员出现的顺序乱,或者某个成员不存在,或者多了个“{”或“,”等,哪些异常情况需要支持,哪些不需要支持,只需要报错就好了。
异常的情况比较多,不容易一次考虑全,可以参考下别人是怎么做的。建议使用网上的Json在线解析工具做一些异常测试,再来决定如何处理。网上的在线解析工具,可以参考这个:
http://json.cn/
我是用了一个比较复杂的数组,成员为结构体的数组,如下:
{
"people":[
{"firstName":"z","lastName":"Jason","email":"[email protected]","height":1.67},
{"lastName":"jadena","email":"[email protected]","age":8,"height":1.17},
{"email":"[email protected]","firstName":"z","lastName":"Juliet","age":36,"height":1.55}
]
}
注意看下,这个数组中,其成员结构体应该还是同上面定义的结构体people一致,但是,其中某些项是缺少的,某些成员的出现顺序是错乱的,这也是对我们程序的一种测试。
对于结构体为成员的数组,需要考虑的东西就比较多了,我大体列了几项:
1, 如何实现某个key的重复出现的解析?
首先看看“lastName”,它是在每个对象中出现一次的,也就是说,第一个“lastName”赋值给第一个对象,第二个“lastName”赋值给第二个对象,如此类推。
2, 对于缺少的成员,如何处理?
例如“firstName”,在第二个对象中就缺少了,此时,绝不能搜索第三个对象中的“firstName”来填充第二个对象。这里就出来一个问题,怎么判断第二个对象的结束?
每个对象都是有边界的,由“{”开始,到“}”结束。对于第二个对象,若搜索到结束处,还没有“firstName”,那么,就认为是读取不到该值了。此时,并不是认为Json串有格式问题,而是给相应值赋上默认值,继续往后解析。
3, 如何界定数组的结束?
每个数组都是有边界的,由“[”开始,到“]”结束。
4, 对于更复杂的情况,例如,数组的成员是结构体,在结构体中又有数组,该如何解析呢?
两种方法:
1)先定义一个解析的规则,或者说模板,然后照着模板来解析。对于解析规则,要考虑到兼容性:
若添加了新项,旧的解析规则应该能工作,只是不解析新添加成员;
对于旧json串,新的解析规则应该能工作,只是新添加成员不能赋值;
2)使用一种标准的解析方法,能够实现互相嵌套解析。
目前,我使用了第一种方案,因为这样对于编程来说要简单一些。如果想做一个通用接口,还是应该选择第二种方案来做。
整体判断是否符合json串的规则,我整理了几项:
1,“{”与“}”的个数,应该是匹配的(需要去掉字符串值中的{});
2,“[”与“]”应该匹配(需要去掉字符串值中的[]);
3,“"”是成对的(需要去掉字符串内容中的");
4,每个键值对中有一个“:”;
5,两个同级相邻的键值对(或者数组元素、对象)之间是“,”;
6,判断过程中要考虑字符串的规则,尤其是特殊字符;
7,判断过程中要考虑数字的规则;
在代码实现中,我使用了前3项来进行有效性判断,并不完整,但是也能做一个初步的判断了。
关于效率问题,其实是与实现方案密切相关的,我在编程过程中考虑过3种方案:
1, 在整个Json串中进行逐个key的搜索;
----效率不高,实现逻辑比较简单。这是我第一次选择的方案,但是存在问题:若数组内某个对象缺少某个成员的赋值,则该key值不存在,会导致搜索下一个对象的,从而导致赋值错误。
2, 从json串的头开始解析,循序往后;
----效率高,但是这个解析方法的实现,比较复杂。里面还涉及递归调用的问题,我还没有考虑明白,暂时不采用。
3, 对每个结构体内容的逐个搜索;
对于一个结构体,知道开始与结束,就有了边界,可以解决第1个方案中的成员缺少问题。这也是我目前采用的方案,效率介于以上两个方案之间。
本文所附带例程,只是一个学习之作,对于初学Json的同学,可以有些借鉴参考的作用。但是,对于异常处理、字符串中的特殊字符的处理等,都不完善,解析方法也不是做成一个通用的接口,只能根据具体的协议类型,来定制一个解析函数,在使用过程中受限较多,所以,并不能真正用于软件工程开发中。
真正用于软件工程开发中的Json接口,C语言版本的大家可以参考CJSON,下载地址为http://sourceforge.net/projects/cjson/。至于其他语言的,可以到 http://www.json.org.cn/ 中去寻找相应的实现。
#include
#include
#include
#include
#include
#define PRINT(msg...) do {printf("lintax: "); printf(msg);}while(0)
typedef struct
{
int id;
char firstName[32];
char lastName[32];
char email[64];
int age;
float height;
}people;
//在缓冲buf 中寻找字符串str
//this func could find something in bin (not only in str)
int find_str_in_buf(char * buf,int len,char * str,int len_str)
{
int pos_buf=0;
char * buf_tmp=buf;
int i,num;
if((buf==NULL)||(str==NULL))
{
return -1;
}
while(pos_buf+len_str<=len)
{
num=0;
for(i=0;i=0)
{
pos_str+=pos;
}
else
{
PRINT("find key failed\n");
return -1;
}
//2,find the key end "
//跳过key的长度
pos_str+=len_key;
pos=find_str_in_buf(json_str+pos_str, str_len-pos_str, "\"", 1);
//PRINT("%s: find the key end \" pos_str=%d, pos=%d\n",__func__,pos_str, pos);
//PRINT("str is :%s\n",json_str+pos_str);
if(pos>=0)
{
pos_str+=pos;
}
else
{
PRINT("find the key end \" failed\n");
return -1;
}
//3,find :
pos_str+=pos+1;
pos=find_str_in_buf(json_str+pos_str, str_len-pos_str, ":", 1);
//PRINT("%s: find : pos_str=%d, pos=%d\n",__func__,pos_str, pos);
//PRINT("str is :%s\n",json_str+pos_str+1);
if(pos>=0)
{
pos_str+=pos;
}
else
{
PRINT("find : failed\n");
return -1;
}
//PRINT("after find , to return: %d\n", pos_str+1);
return (pos_str+1);
}
int get_int_from_json_str(char * json_str, int str_len, char * str_key, int len_key, int *value)
{
int pos_str=0;
if ( (json_str==NULL)||(str_key==NULL)||(value==NULL))
{
return -1;
}
//1,find value pos
pos_str=find_value_from_json_str(json_str, str_len, str_key, len_key);
if(pos_str<0)
{
PRINT("find value failed\n");
return -1;
}
//2,change the string to int
int value_int=atoi(json_str+pos_str);
*value=value_int;
//PRINT("after memcpy str_value, to return: %d\n", pos_str);
return (pos_str);
}
int get_float_from_json_str(char * json_str, int str_len, char * str_key, int len_key, float *value)
{
int pos_str=0;
if ( (json_str==NULL)||(str_key==NULL)||(value==NULL))
{
return -1;
}
//1,find key value
pos_str=find_value_from_json_str(json_str, str_len, str_key, len_key);
if(pos_str<0)
{
PRINT("find value failed\n");
return -1;
}
//2,change the string to float
float value_float=atof(json_str+pos_str);
*value=value_float;
//PRINT("after memcpy str_value, to return: %d\n", pos_str);
return (pos_str);
}
int get_str_from_json_str(char * json_str, int str_len, char * str_key, int len_key, char * str_value, int * plen_value)
{
int pos_str=0;
int pos_start=0;
int pos=0;
int size_value=*plen_value;
if ( (json_str==NULL)||(str_key==NULL)||(str_value==NULL))
{
str_value=NULL;
return -1;
}
//1,find key value
pos_str=find_value_from_json_str(json_str, str_len, str_key, len_key);
if(pos_str<0)
{
PRINT("find value failed\n");
str_value=NULL;
return -1;
}
//2,find the value start "
pos=find_str_in_buf(json_str+pos_str, str_len-pos_str, "\"", 1);
//PRINT("%s: find the value start \" pos_str=%d, pos=%d\n",__func__,pos_str, pos);
//PRINT("str is :%s\n",json_str+pos_str);
if(pos>=0)
{
pos_str+=pos;
pos_start=pos_str+1;
}
else
{
PRINT("find the value start \" failed\n");
str_value=NULL;
return -1;
}
//3,find the value end "
pos_str+=pos+1;
pos=find_str_in_buf(json_str+pos_str, str_len-pos_str, "\"", 1);
//PRINT("%s: find the value end \" pos_str=%d, pos=%d\n",__func__,pos_str, pos);
//PRINT("str is :%s\n",json_str+pos_str);
if(pos>=0)
{
pos_str+=pos;
}
else
{
PRINT("find the value end \" failed\n");
str_value=NULL;
return -1;
}
if(pos_str-pos_start>=size_value)
{
*plen_value=size_value-1;
}
else
{
*plen_value=pos_str-pos_start;
}
//PRINT("to memcpy str_value: pos_str-pos_start=%d, *plen_value=%d\n",pos_str-pos_start,*plen_value);
memcpy(str_value, json_str+pos_start, *plen_value);
str_value[*plen_value]=0;
//PRINT("after memcpy str_value, to return: %d\n", pos_str+1);
return (pos_str+1);
}
int main(int argc, char **argv)
{
int ret=-1, i;
char str1[4096], *str;
int len_find=0;
int pos;
int len_value;
FILE *fp;
int flen1 = 0;
char file_path[100];
people workers[10];
//parse json string
sprintf(file_path,"json_str3.txt");// json_str1.txt, json_str2.txt, json_str3.txt, json_str4.txt
fp = fopen(file_path, "r");
if (NULL == fp)
{
printf("open file %s fail : %m.\n", file_path);
return ret;
}
fseek(fp,0,SEEK_END); /* 定位到文件末尾 */
flen1=ftell(fp); /* 得到文件大小 */
fseek(fp,0,SEEK_SET); /* 定位到文件开头 */
ret=fread(str1,flen1,1,fp); /* 一次性读取全部文件内容 */ /**/
fclose(fp);
if(ret<=0)
{
PRINT("read file %s failed\n",file_path);
return -1;
}
printf("after read file %s .\n", file_path);
ret=judge_json_format(str1, flen1);//whether meet the Json standards
if(ret<0)
{
PRINT("the json str is invalid\n");
return -1;
}
else
{
PRINT("the json str is valid\n\n");
}
pos=0;
str=str1;
len_find=flen1;
pos=find_value_from_json_str(str, len_find, "people", strlen("people"));
if(pos<0)
{
PRINT("find people failed \n");
return -1;
}
else
{
PRINT("find people successful .\n" );
}
str=str+pos;
len_find-=pos;
pos=find_str_in_buf(str, len_find, "[", strlen("[")); //
if(pos<0)
{
PRINT("find [ failed \n");
return -1;
}
else
{
PRINT("find [ successful .\n" );
}
str=str+pos;
len_find-=(pos+1);
for(i=0;i<3;i++)
{
//需要定义一个边界,避免搜索到下一个结构体中的内容(当前结构体中缺少某成员的赋值)。每个结构体都是{}为边界。
int pos_struct_start=0;
int pos_struct_end=0;
int len_find_struct=0;
pos=find_str_in_buf(str, len_find, "{", strlen("{")); //
if(pos<0)
{
PRINT("find { failed \n");
return -1;
}
else
{
PRINT("find { successful .\n" );
}
str=str+pos+1;
len_find-=(pos+1);
pos_struct_start=0;//the pos in str now.
pos=find_str_in_buf(str, len_find, "}", strlen("}")); //
if(pos<0)
{
PRINT("find } failed \n");
return -1;
}
else
{
PRINT("find } successful .\n" );
}
//str=str+pos;//just find the pos,not change str pos
pos_struct_end=pos-1;
len_find-=(pos+1);
len_find_struct = pos_struct_end - pos_struct_start + 1;
workers[i].id=i;
len_value=sizeof(workers[i].firstName);
pos=get_str_from_json_str(str, len_find_struct, "firstName", strlen("firstName"), workers[i].firstName, &len_value);
if(pos<0)
{
PRINT("read firstName failed , i=%d\n",i);
}
else
{
PRINT("id=%d, firstName is %s .\n", i,workers[i].firstName);
}
len_value=sizeof(workers[i].lastName);
pos=get_str_from_json_str(str, len_find_struct, "lastName", strlen("lastName"), workers[i].lastName, &len_value);//str1+strlen("firstName")
if(pos<0)
{
PRINT("read lastName failed , i=%d\n",i);
}
else
{
PRINT("id=%d, lastName is %s .\n", i,workers[i].lastName);
}
len_value=sizeof(workers[i].email);
pos=get_str_from_json_str(str, len_find_struct, "email", strlen("email"), workers[i].email, &len_value);//str1+strlen("lastName")
if(pos<0)
{
PRINT("read email failed , i=%d\n",i);
}
else
{
PRINT("id=%d, email is %s .\n", i,workers[i].email);
}
pos=get_int_from_json_str(str, len_find_struct, "age", strlen("age"), &(workers[i].age));
if(pos<0)
{
PRINT("read age failed , i=%d\n",i);
}
else
{
PRINT("id=%d, age is %d .\n", i,workers[i].age);
}
pos=get_float_from_json_str(str, len_find_struct, "height", strlen("height"), &(workers[i].height));
if(pos<0)
{
PRINT("read height failed , i=%d\n",i);
}
else
{
PRINT("id=%d, height is %.02f .\n", i,workers[i].height);
}
PRINT("id=%d, len_find_struct=%d .\n", i,len_find_struct);
str=str+len_find_struct+1;//
}
return 0;
}